blob: c0c17fe1e24115404e225dd1286c6bc37cec4782 [file] [log] [blame]
/*
* Copyright (c) 2018-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <iostream>
#include <functional>
#include "pas_config.h"
#include "pas_heap.h"
#include "pas_heap_runtime_config.h"
#include "pas_object_kind.h"
#include "pas_page_sharing_mode.h"
#include "pas_scavenger.h"
#include "pas_string_stream.h"
#include <set>
#include <sstream>
#include <string>
#include <vector>
// We used to be able to turn TLCs and segregated heaps on and off. We may bring that back. For
// now, keep the old code in the tests that knew what to do for both. New code can just assume
// that both of these are true.
#define TLC 1
#define SEGHEAP 1
void* allocationConfigAllocate(size_t size, const char* name, pas_allocation_kind allocationKind, void* arg);
void allocationConfigDeallocate(void* ptr, size_t size, pas_allocation_kind allocationKind, void* arg);
extern const pas_allocation_config allocationConfig;
inline void stringStreamConstruct(pas_string_stream* stream)
{
pas_string_stream_construct(stream, &allocationConfig);
}
template<typename T, typename Func>
inline std::string dumpToString(const T& value, const Func& func)
{
pas_string_stream stream;
stringStreamConstruct(&stream);
func(value, &stream.base);
std::string result = pas_string_stream_get_string(&stream);
pas_string_stream_destruct(&stream);
return result;
}
inline std::ostream& operator<<(std::ostream& out, pas_page_sharing_mode mode)
{
out << pas_page_sharing_mode_get_string(mode);
return out;
}
inline std::ostream& operator<<(std::ostream& out, pas_segregated_page_config_variant variant)
{
out << pas_segregated_page_config_variant_get_string(variant);
return out;
}
inline std::ostream& operator<<(std::ostream& out, pas_object_kind kind)
{
out << pas_object_kind_get_string(kind);
return out;
}
inline std::ostream& operator<<(std::ostream& out, pas_page_kind kind)
{
out << pas_page_kind_get_string(kind);
return out;
}
#define CHECK(expression) do { \
if ((expression)) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #expression << " failed." << std::endl; \
abort(); \
} while (false)
#define CHECK_EQUAL(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue == _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " == " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_NOT_EQUAL(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue != _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " != " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_GREATER(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue > _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " > " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_GREATER_EQUAL(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue >= _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " >= " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_LESS(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue < _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " < " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_LESS_EQUAL(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (_aValue <= _bValue) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " <= " << #b << " failed:" << std::endl; \
std::cout << " " << #a << " = " << _aValue << std::endl; \
std::cout << " " << #b << " = " << _bValue << std::endl; \
abort(); \
} while (false)
#define CHECK_ALIGNMENT_EQUAL(a, b) do { \
auto _aValue = (a); \
auto _bValue = (b); \
if (pas_alignment_is_equal(_aValue, _bValue)) \
break; \
std::cout << __FILE__ << ":" << __LINE__ << ": " << __PRETTY_FUNCTION__ << " assertion " \
<< #a << " == " << #b << " failed:" << std::endl; \
pas_string_stream stream; \
stringStreamConstruct(&stream); \
pas_alignment_dump(_aValue, reinterpret_cast<pas_stream*>(&stream)); \
std::cout << " " << #a << " = " << pas_string_stream_get_string(&stream) << std::endl; \
pas_string_stream_reset(&stream); \
pas_alignment_dump(_bValue, reinterpret_cast<pas_stream*>(&stream)); \
std::cout << " " << #b << " = " << pas_string_stream_get_string(&stream) << std::endl; \
pas_string_stream_destruct(&stream); \
abort(); \
} while (false)
void testSucceeded();
void addTest(const std::string& file, int line,
const std::string& name, std::function<void()> test);
// This is an improperly balanced, low-quality random number. But it uses a deterministic seed
// and its behavior is super easy to understand. This makes it ideal for tests that want to do
// random things but want to be reproducible.
unsigned deterministicRandomNumber(unsigned exclusiveUpperBound);
#define ADD_TEST(test) addTest(__FILE__, __LINE__, #test, [=] () { test; })
#define SKIP_TEST(test) do { if ((false)) ADD_TEST(test); } while (false)
struct TestScopeImpl;
class TestScope {
public:
TestScope(const std::string& name,
std::function<void()> setUp);
~TestScope();
private:
TestScopeImpl* m_impl;
};
#define ADD_GROUP(command) ({ \
TestScope testScope(#command, [] () {}); \
command; \
})
class RuntimeConfigTestScope : public TestScope {
public:
RuntimeConfigTestScope(const std::string& name,
std::function<void(pas_heap_runtime_config&)> setUp);
};
class ForceExclusives : public RuntimeConfigTestScope {
public:
ForceExclusives();
};
class ForceTLAs : public RuntimeConfigTestScope {
public:
ForceTLAs();
};
class ForceBitfit : public RuntimeConfigTestScope {
public:
ForceBitfit();
};
class DisableBitfit : public RuntimeConfigTestScope {
public:
DisableBitfit();
};
class ForcePartials : public RuntimeConfigTestScope {
public:
ForcePartials();
};
class ForceBaselines : public RuntimeConfigTestScope {
public:
ForceBaselines();
};
class VerifyGranules : public TestScope {
public:
VerifyGranules();
};
extern pas_scavenger_synchronous_operation_kind scavengeKind;
class RunScavengerFully : public TestScope {
public:
RunScavengerFully();
};
class RunScavengerOnNonRemoteCaches : public TestScope {
public:
RunScavengerOnNonRemoteCaches();
};
class SuspendScavengerScope : public TestScope {
public:
SuspendScavengerScope();
};
class InstallVerifier : public TestScope {
public:
InstallVerifier();
};
class EpochIsCounter : public TestScope {
public:
EpochIsCounter();
};
class BootJITHeap : public TestScope {
public:
BootJITHeap();
};
class EnablePageBalancing : public TestScope {
public:
EnablePageBalancing();
};
class DisablePageBalancing : public TestScope {
public:
DisablePageBalancing();
};
class DecommitZeroFill : public TestScope {
public:
DecommitZeroFill();
};
bool hasScope(const std::string& filter);
void dumpObjectSet(const std::set<void*>& objects);
void dumpObjectsInHeap(pas_heap* heap);
void dumpObjectsInHeaps(const std::vector<pas_heap*>& heaps);
void forEachLiveObject(pas_heap* heap, std::function<bool(void*, size_t)> callback);
void verifyMinimumObjectDistance(const std::set<void*>& objects,
size_t minimumDistance);
void verifyExactObjectDistance(const std::set<void*>& objects,
size_t exactDistance);
void scavenge();
void printStatusReport();