tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestPHC.cpp (18402B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "gtest/gtest.h"
      6 
      7 #include "mozmemory.h"
      8 #include "mozilla/Assertions.h"
      9 #include "mozilla/mozalloc.h"
     10 #include "mozilla/StaticPrefs_memory.h"
     11 #include "PHC.h"
     12 
     13 using namespace mozilla;
     14 
     15 bool PHCInfoEq(phc::AddrInfo& aInfo, phc::AddrInfo::Kind aKind, void* aBaseAddr,
     16               size_t aUsableSize, bool aHasAllocStack, bool aHasFreeStack) {
     17  return aInfo.mKind == aKind && aInfo.mBaseAddr == aBaseAddr &&
     18         aInfo.mUsableSize == aUsableSize &&
     19         // Proper stack traces will have at least 3 elements.
     20         (aHasAllocStack ? (aInfo.mAllocStack->mLength > 2)
     21                         : (aInfo.mAllocStack.isNothing())) &&
     22         (aHasFreeStack ? (aInfo.mFreeStack->mLength > 2)
     23                        : (aInfo.mFreeStack.isNothing()));
     24 }
     25 
     26 bool JeInfoEq(jemalloc_ptr_info_t& aInfo, PtrInfoTag aTag, void* aAddr,
     27              size_t aSize, arena_id_t arenaId) {
     28  return aInfo.tag == aTag && aInfo.addr == aAddr && aInfo.size == aSize
     29 #ifdef MOZ_DEBUG
     30         && aInfo.arenaId == arenaId
     31 #endif
     32      ;
     33 }
     34 
     35 uint8_t* GetPHCAllocation(size_t aSize, size_t aAlignment = 1) {
     36  // A crude but effective way to get a PHC allocation.
     37  for (int i = 0; i < 2000000; i++) {
     38    void* p = (aAlignment == 1) ? moz_xmalloc(aSize)
     39                                : moz_xmemalign(aAlignment, aSize);
     40    if (mozilla::phc::IsPHCAllocation(p, nullptr)) {
     41      return (uint8_t*)p;
     42    }
     43    free(p);
     44  }
     45  return nullptr;
     46 }
     47 
     48 #if defined(XP_DARWIN) && defined(__aarch64__)
     49 static const size_t kPageSize = 16384;
     50 #else
     51 static const size_t kPageSize = 4096;
     52 #endif
     53 
     54 TEST(PHC, TestPHCAllocations)
     55 {
     56  mozilla::phc::SetPHCState(phc::PHCState::Enabled);
     57 
     58  // First, check that allocations of various sizes all get put at the end of
     59  // their page as expected. Also, check their sizes are as expected.
     60 
     61 #define ASSERT_POS(n1, n2)                                      \
     62  p = (uint8_t*)moz_xrealloc(p, (n1));                          \
     63  ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
     64            kPageSize - (n2));                                  \
     65  ASSERT_EQ(moz_malloc_usable_size(p), (n2));
     66 
     67  uint8_t* p = GetPHCAllocation(1);
     68  if (!p) {
     69    MOZ_CRASH("failed to get a PHC allocation");
     70  }
     71 
     72  // The smallest possible allocation is 16 bytes.
     73  ASSERT_POS(8U, 16U);
     74  for (unsigned i = 16; i <= kPageSize; i *= 2) {
     75    ASSERT_POS(i, i);
     76  }
     77 
     78  free(p);
     79 
     80 #undef ASSERT_POS
     81 
     82  // Second, do similar checking with allocations of various alignments. Also
     83  // check that their sizes (which are different to allocations with normal
     84  // alignment) are the same as the sizes of equivalent non-PHC allocations.
     85 
     86 #define ASSERT_ALIGN(a1, a2)                                    \
     87  p = (uint8_t*)GetPHCAllocation(8, (a1));                      \
     88  ASSERT_EQ((reinterpret_cast<uintptr_t>(p) & (kPageSize - 1)), \
     89            kPageSize - (a2));                                  \
     90  ASSERT_EQ(moz_malloc_usable_size(p), (a2));                   \
     91  free(p);                                                      \
     92  p = (uint8_t*)moz_xmemalign((a1), 8);                         \
     93  ASSERT_EQ(moz_malloc_usable_size(p), (a2));                   \
     94  free(p);
     95 
     96  // The smallest possible allocation is 16 bytes.
     97  ASSERT_ALIGN(8U, 16U);
     98  for (unsigned i = 16; i <= kPageSize; i *= 2) {
     99    ASSERT_ALIGN(i, i);
    100  }
    101 
    102 #undef ASSERT_ALIGN
    103 }
    104 
    105 static void TestInUseAllocation(uint8_t* aPtr, size_t aSize) {
    106  phc::AddrInfo phcInfo;
    107  jemalloc_ptr_info_t jeInfo;
    108 
    109  // Test an in-use PHC allocation: first byte within it.
    110  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr, &phcInfo));
    111  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
    112                        true, false));
    113  ASSERT_EQ(moz_malloc_usable_size(aPtr), aSize);
    114  jemalloc_ptr_info(aPtr, &jeInfo);
    115  ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, aPtr, aSize, 0));
    116 
    117  // Test an in-use PHC allocation: last byte within it.
    118  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize - 1, &phcInfo));
    119  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
    120                        true, false));
    121  ASSERT_EQ(moz_malloc_usable_size(aPtr + aSize - 1), aSize);
    122  jemalloc_ptr_info(aPtr + aSize - 1, &jeInfo);
    123  ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, aPtr, aSize, 0));
    124 
    125  // Test an in-use PHC allocation: last byte before it.
    126  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr - 1, &phcInfo));
    127  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
    128                        true, false));
    129  ASSERT_EQ(moz_malloc_usable_size(aPtr - 1), 0ul);
    130  jemalloc_ptr_info(aPtr - 1, &jeInfo);
    131  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    132 
    133  // Test an in-use PHC allocation: first byte on its allocation page.
    134  ASSERT_TRUE(
    135      mozilla::phc::IsPHCAllocation(aPtr + aSize - kPageSize, &phcInfo));
    136  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, aPtr, aSize,
    137                        true, false));
    138  jemalloc_ptr_info(aPtr + aSize - kPageSize, &jeInfo);
    139  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    140 
    141  // Test an in-use PHC allocation: first byte in the following guard page.
    142  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize, &phcInfo));
    143  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    144                        true, false));
    145  jemalloc_ptr_info(aPtr + aSize, &jeInfo);
    146  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    147 
    148  // Test an in-use PHC allocation: last byte in the lower half of the
    149  // following guard page.
    150  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize + (kPageSize / 2 - 1),
    151                                            &phcInfo));
    152  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    153                        true, false));
    154  jemalloc_ptr_info(aPtr + aSize + (kPageSize / 2 - 1), &jeInfo);
    155  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    156 
    157  // Test an in-use PHC allocation: last byte in the preceding guard page.
    158  ASSERT_TRUE(
    159      mozilla::phc::IsPHCAllocation(aPtr + aSize - 1 - kPageSize, &phcInfo));
    160  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    161                        true, false));
    162  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize, &jeInfo);
    163  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    164 
    165  // Test an in-use PHC allocation: first byte in the upper half of the
    166  // preceding guard page.
    167  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(
    168      aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1), &phcInfo));
    169  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    170                        true, false));
    171  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1),
    172                    &jeInfo);
    173  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    174 }
    175 
    176 static void TestFreedAllocation(uint8_t* aPtr, size_t aSize) {
    177  phc::AddrInfo phcInfo;
    178  jemalloc_ptr_info_t jeInfo;
    179 
    180  // Test a freed PHC allocation: first byte within it.
    181  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr, &phcInfo));
    182  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
    183                        true, true));
    184  jemalloc_ptr_info(aPtr, &jeInfo);
    185  ASSERT_TRUE(JeInfoEq(jeInfo, TagFreedAlloc, aPtr, aSize, 0));
    186 
    187  // Test a freed PHC allocation: last byte within it.
    188  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize - 1, &phcInfo));
    189  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
    190                        true, true));
    191  jemalloc_ptr_info(aPtr + aSize - 1, &jeInfo);
    192  ASSERT_TRUE(JeInfoEq(jeInfo, TagFreedAlloc, aPtr, aSize, 0));
    193 
    194  // Test a freed PHC allocation: last byte before it.
    195  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr - 1, &phcInfo));
    196  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
    197                        true, true));
    198  jemalloc_ptr_info(aPtr - 1, &jeInfo);
    199  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    200 
    201  // Test a freed PHC allocation: first byte on its allocation page.
    202  ASSERT_TRUE(
    203      mozilla::phc::IsPHCAllocation(aPtr + aSize - kPageSize, &phcInfo));
    204  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::FreedPage, aPtr, aSize,
    205                        true, true));
    206  jemalloc_ptr_info(aPtr + aSize - kPageSize, &jeInfo);
    207  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    208 
    209  // Test a freed PHC allocation: first byte in the following guard page.
    210  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize, &phcInfo));
    211  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    212                        true, true));
    213  jemalloc_ptr_info(aPtr + aSize, &jeInfo);
    214  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    215 
    216  // Test a freed PHC allocation: last byte in the lower half of the following
    217  // guard page.
    218  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(aPtr + aSize + (kPageSize / 2 - 1),
    219                                            &phcInfo));
    220  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    221                        true, true));
    222  jemalloc_ptr_info(aPtr + aSize + (kPageSize / 2 - 1), &jeInfo);
    223  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    224 
    225  // Test a freed PHC allocation: last byte in the preceding guard page.
    226  ASSERT_TRUE(
    227      mozilla::phc::IsPHCAllocation(aPtr + aSize - 1 - kPageSize, &phcInfo));
    228  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    229                        true, true));
    230  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize, &jeInfo);
    231  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    232 
    233  // Test a freed PHC allocation: first byte in the upper half of the preceding
    234  // guard page.
    235  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(
    236      aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1), &phcInfo));
    237  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, aPtr, aSize,
    238                        true, true));
    239  jemalloc_ptr_info(aPtr + aSize - 1 - kPageSize - (kPageSize / 2 - 1),
    240                    &jeInfo);
    241  ASSERT_TRUE(JeInfoEq(jeInfo, TagUnknown, nullptr, 0, 0));
    242 }
    243 
    244 TEST(PHC, TestPHCInfo)
    245 {
    246  mozilla::phc::SetPHCState(phc::PHCState::Enabled);
    247 
    248  int stackVar = 0;
    249  phc::AddrInfo phcInfo;
    250 
    251  // Test a default AddrInfo.
    252  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0ul,
    253                        false, false));
    254 
    255  // Test some non-PHC allocation addresses.
    256  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(nullptr, &phcInfo));
    257  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0,
    258                        false, false));
    259  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(&stackVar, &phcInfo));
    260  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::Unknown, nullptr, 0,
    261                        false, false));
    262 
    263  uint8_t* p = GetPHCAllocation(32);
    264  if (!p) {
    265    MOZ_CRASH("failed to get a PHC allocation");
    266  }
    267 
    268  TestInUseAllocation(p, 32);
    269 
    270  free(p);
    271 
    272  TestFreedAllocation(p, 32);
    273 
    274  // There are no tests for `mKind == NeverAllocatedPage` because it's not
    275  // possible to reliably get ahold of such a page.
    276 }
    277 
    278 TEST(PHC, TestPHCDisablingThread)
    279 {
    280  mozilla::phc::SetPHCState(phc::PHCState::Enabled);
    281 
    282  uint8_t* p = GetPHCAllocation(32);
    283  uint8_t* q = GetPHCAllocation(32);
    284  if (!p || !q) {
    285    MOZ_CRASH("failed to get a PHC allocation");
    286  }
    287 
    288  ASSERT_TRUE(mozilla::phc::IsPHCEnabledOnCurrentThread());
    289  mozilla::phc::DisablePHCOnCurrentThread();
    290  ASSERT_FALSE(mozilla::phc::IsPHCEnabledOnCurrentThread());
    291 
    292  // Test realloc() on a PHC allocation while PHC is disabled on the thread.
    293  uint8_t* p2 = (uint8_t*)realloc(p, 128);
    294  // The small realloc is fulfilled within the same page, but it does move.
    295  ASSERT_TRUE(p2 == p - 96);
    296  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(p2, nullptr));
    297  uint8_t* p3 = (uint8_t*)realloc(p2, 2 * kPageSize);
    298  // The big realloc is not in-place, and the result is not a PHC allocation.
    299  ASSERT_TRUE(p3 != p2);
    300  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p3, nullptr));
    301  free(p3);
    302 
    303  // Test free() on a PHC allocation while PHC is disabled on the thread.
    304  free(q);
    305 
    306  // These must not be PHC allocations.
    307  uint8_t* r = GetPHCAllocation(32);  // This will fail.
    308  ASSERT_FALSE(!!r);
    309 
    310  mozilla::phc::ReenablePHCOnCurrentThread();
    311  ASSERT_TRUE(mozilla::phc::IsPHCEnabledOnCurrentThread());
    312 
    313  // If it really was reenabled we should be able to get PHC allocations
    314  // again.
    315  uint8_t* s = GetPHCAllocation(32);  // This should succeed.
    316  ASSERT_TRUE(!!s);
    317  free(s);
    318 }
    319 
    320 TEST(PHC, TestPHCDisablingGlobal)
    321 {
    322  mozilla::phc::SetPHCState(phc::PHCState::Enabled);
    323 
    324  uint8_t* p1 = GetPHCAllocation(32);
    325  uint8_t* p2 = GetPHCAllocation(32);
    326  uint8_t* q = GetPHCAllocation(32);
    327  if (!p1 || !p2 || !q) {
    328    MOZ_CRASH("failed to get a PHC allocation");
    329  }
    330 
    331  mozilla::phc::SetPHCState(phc::PHCState::OnlyFree);
    332 
    333  // Test realloc() on a PHC allocation while PHC is disabled on the thread.
    334  uint8_t* p3 = (uint8_t*)realloc(p1, 128);
    335  // The small realloc is evicted from PHC because in "OnlyFree" state PHC
    336  // tries to reduce its memory impact.
    337  ASSERT_TRUE(p3 != p1);
    338  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p3, nullptr));
    339  free(p3);
    340  uint8_t* p4 = (uint8_t*)realloc(p2, 2 * kPageSize);
    341  // The big realloc is not in-place, and the result is not a PHC
    342  // allocation, regardless of PHC's state.
    343  ASSERT_TRUE(p4 != p2);
    344  ASSERT_FALSE(mozilla::phc::IsPHCAllocation(p4, nullptr));
    345  free(p4);
    346 
    347  // Test free() on a PHC allocation while PHC is disabled on the thread.
    348  free(q);
    349 
    350  // These must not be PHC allocations.
    351  uint8_t* r = GetPHCAllocation(32);  // This will fail.
    352  ASSERT_FALSE(!!r);
    353 
    354  mozilla::phc::SetPHCState(phc::PHCState::Enabled);
    355 
    356  // If it really was reenabled we should be able to get PHC allocations
    357  // again.
    358  uint8_t* s = GetPHCAllocation(32);  // This should succeed.
    359  ASSERT_TRUE(!!s);
    360  free(s);
    361 }
    362 
    363 size_t GetNumAvailable() {
    364  mozilla::phc::PHCStats stats;
    365  GetPHCStats(stats);
    366  return stats.mSlotsFreed + stats.mSlotsUnused;
    367 }
    368 
    369 #ifndef ANDROID
    370 static void TestExhaustion(std::vector<uint8_t*>& aAllocs) {
    371  // Disable the reuse delay to make the test more reliable.  At the same
    372  // time lower the other probabilities to speed up this test, but much
    373  // lower and the test runs more slowly maybe because of how PHC
    374  // optimises for multithreading.
    375  mozilla::phc::SetPHCProbabilities(64, 64, 0);
    376 
    377  for (auto& a : aAllocs) {
    378    a = GetPHCAllocation(128);
    379    ASSERT_TRUE(a);
    380    TestInUseAllocation(a, 128);
    381  }
    382 
    383  // No more PHC slots are available.
    384  ASSERT_EQ(0ul, GetNumAvailable());
    385  // We should now fail to get an allocation.
    386  ASSERT_FALSE(GetPHCAllocation(128));
    387 
    388  // Restore defaults.
    389  mozilla::phc::SetPHCProbabilities(
    390      StaticPrefs::memory_phc_avg_delay_first(),
    391      StaticPrefs::memory_phc_avg_delay_normal(),
    392      StaticPrefs::memory_phc_avg_delay_page_reuse());
    393 }
    394 
    395 static void ReleaseVector(std::vector<uint8_t*>& aAllocs) {
    396  for (auto& a : aAllocs) {
    397    free(a);
    398    TestFreedAllocation(a, 128);
    399  }
    400 }
    401 
    402 TEST(PHC, TestExhaustion)
    403 {
    404  // PHC hardcodes the amount of allocations to track.
    405  size_t num_allocations = GetNumAvailable();
    406  mozilla::phc::DisablePHCOnCurrentThread();
    407  std::vector<uint8_t*> allocations(num_allocations);
    408  mozilla::phc::ReenablePHCOnCurrentThread();
    409 
    410  TestExhaustion(allocations);
    411  ReleaseVector(allocations);
    412 
    413  // And now that we've released those allocations the number of available
    414  // slots will be non-zero.
    415  ASSERT_TRUE(GetNumAvailable() != 0);
    416  // And we should be able to get new allocations again.
    417  uint8_t* r = GetPHCAllocation(128);
    418  ASSERT_TRUE(!!r);
    419  free(r);
    420 }
    421 
    422 static uint8_t* VectorMax(std::vector<uint8_t*>& aVec) {
    423  uint8_t* max = 0;
    424  for (auto a : aVec) {
    425    max = a > max ? a : max;
    426  }
    427  return max;
    428 }
    429 
    430 TEST(PHC, TestBounds)
    431 {
    432  // PHC reserves a large area of virtual memory and depending on runtime
    433  // configuration allocates a smaller range of memory within it.  This test
    434  // tests that boundary.
    435 
    436  size_t num_allocations = GetNumAvailable();
    437  mozilla::phc::DisablePHCOnCurrentThread();
    438  std::vector<uint8_t*> allocations(num_allocations);
    439  mozilla::phc::ReenablePHCOnCurrentThread();
    440 
    441  TestExhaustion(allocations);
    442 
    443  uint8_t* max = VectorMax(allocations);
    444  ASSERT_TRUE(!!max);
    445 
    446  // Verify that the next page is a guard page.
    447  phc::AddrInfo phcInfo;
    448  ASSERT_TRUE(mozilla::phc::IsPHCAllocation(max + 128 + 1, &phcInfo));
    449  ASSERT_TRUE(PHCInfoEq(phcInfo, phc::AddrInfo::Kind::GuardPage, max, 128, true,
    450                        false));
    451 
    452  // But the page after that is Nothing.
    453  ASSERT_TRUE(!mozilla::phc::IsPHCAllocation(max + kPageSize * 2, &phcInfo));
    454 
    455  ReleaseVector(allocations);
    456 }
    457 #endif
    458 
    459 size_t GetNumSlotsTotal() {
    460  mozilla::phc::PHCStats stats;
    461  GetPHCStats(stats);
    462  return stats.mSlotsAllocated + stats.mSlotsFreed + stats.mSlotsUnused;
    463 }
    464 
    465 #ifndef ANDROID
    466 TEST(PHC, TestPHCGrow)
    467 {
    468  size_t batch_1_num = GetNumAvailable();
    469  mozilla::phc::DisablePHCOnCurrentThread();
    470  std::vector<uint8_t*> batch_1(batch_1_num);
    471  mozilla::phc::ReenablePHCOnCurrentThread();
    472  TestExhaustion(batch_1);
    473 
    474  size_t total_before = GetNumSlotsTotal();
    475  size_t requested = total_before + 1024;
    476  mozilla::phc::SetPHCSize(requested * kPageSize);
    477 
    478  ASSERT_TRUE(GetNumSlotsTotal() == requested);
    479 
    480  size_t batch_2_num = GetNumAvailable();
    481  ASSERT_TRUE(!!batch_2_num);
    482  ASSERT_TRUE(batch_2_num == 1024ul);
    483  mozilla::phc::DisablePHCOnCurrentThread();
    484  std::vector<uint8_t*> batch_2(batch_2_num);
    485  mozilla::phc::ReenablePHCOnCurrentThread();
    486  TestExhaustion(batch_2);
    487 
    488  ReleaseVector(batch_1);
    489  ReleaseVector(batch_2);
    490 }
    491 #endif
    492 
    493 TEST(PHC, TestPHCLimit)
    494 {
    495  // Test the virtual address limit compiled into PHC.
    496 
    497  size_t start = GetNumSlotsTotal();
    498  mozilla::phc::SetPHCSize(SIZE_MAX);
    499 
    500  ASSERT_TRUE(GetNumSlotsTotal() > start);
    501 
    502  // This needs to match the limit compiled into phc
    503  // (see kPhcVirtualReservation)
    504  size_t limit =
    505 #ifdef HAVE_64BIT_BUILD
    506 #  if defined(XP_DARWIN) && defined(__aarch64__)
    507      512 * 1024 * 1024;
    508 #  else
    509      128 * 1024 * 1024;
    510 #  endif
    511 #else
    512      2 * 1024 * 1024;
    513 #endif
    514  ASSERT_TRUE(GetNumSlotsTotal() == limit / kPageSize / 2 - 1);
    515 
    516  mozilla::phc::SetPHCSize(start);
    517 }