blob: d71dd2691673294b9273967487131ee958ebace8 [file] [log] [blame]
/*
* Copyright (c) 2019 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.
*/
#include "Verifier.h"
#include <iostream>
#include <map>
#include "pas_allocation_callbacks.h"
#include "pas_lock.h"
#include "pas_utils.h"
#include <stdlib.h>
using namespace std;
namespace {
void dumpStateHoldingLock();
#define ASSERT(exp) do { \
if (exp) \
break; \
cerr << "pas Verifier: Assertion " << #exp << " at " << __FILE__ << ":" << __LINE__ \
<< " failed.\n"; \
dumpStateHoldingLock(); \
abort(); \
} while (false)
pas_lock lock = PAS_LOCK_INITIALIZER;
struct Locker {
Locker()
{
pas_lock_lock(&lock);
}
~Locker()
{
pas_lock_unlock(&lock);
}
};
struct Allocation {
Allocation() = default;
Allocation(size_t size,
const char* name,
pas_allocation_kind allocationKind)
: size(size)
, name(name)
, allocationKind(allocationKind)
{
}
size_t size { 0 };
const char* name { nullptr };
pas_allocation_kind allocationKind { pas_object_allocation };
};
typedef map<void*, Allocation> MapType;
MapType allocations[PAS_NUM_HEAP_KINDS];
void allocationCallback(void* resultingBase,
size_t size,
pas_heap_kind heapKind,
const char* name,
pas_allocation_kind allocationKind)
{
Locker locker;
MapType::iterator lowerBoundIter =
allocations[heapKind].lower_bound(resultingBase);
if (lowerBoundIter != allocations[heapKind].end()) {
ASSERT(reinterpret_cast<uintptr_t>(lowerBoundIter->first)
>= reinterpret_cast<uintptr_t>(resultingBase) + size);
}
MapType::iterator leftIter = lowerBoundIter;
if (leftIter != allocations[heapKind].begin()) {
--leftIter;
if (leftIter->first < resultingBase) {
ASSERT(reinterpret_cast<uintptr_t>(leftIter->first) + leftIter->second.size
<= reinterpret_cast<uintptr_t>(resultingBase));
}
}
auto result = allocations[heapKind].emplace(resultingBase,
Allocation(size, name, allocationKind));
ASSERT(result.second);
}
void deallocationCallback(void* base,
size_t size,
pas_heap_kind heapKind,
pas_allocation_kind allocationKind)
{
Locker locker;
MapType::iterator iter =
allocations[heapKind].upper_bound(base);
ASSERT(iter != allocations[heapKind].begin());
--iter;
void* address = iter->first;
Allocation allocation = iter->second;
if (heapKind != pas_bootstrap_free_heap_kind) {
ASSERT(allocationKind == pas_object_allocation);
ASSERT(address == base);
size = allocation.size;
}
ASSERT(reinterpret_cast<uintptr_t>(address)
<= reinterpret_cast<uintptr_t>(base));
ASSERT(reinterpret_cast<uintptr_t>(address) + allocation.size
>= reinterpret_cast<uintptr_t>(base) + size);
ASSERT(allocation.allocationKind == allocationKind);
allocations[heapKind].erase(iter);
if (reinterpret_cast<uintptr_t>(address)
< reinterpret_cast<uintptr_t>(base)) {
ASSERT(allocationKind == pas_delegate_allocation);
auto result = allocations[heapKind].emplace(
address,
Allocation(
reinterpret_cast<uintptr_t>(base) - reinterpret_cast<uintptr_t>(address),
allocation.name,
allocationKind));
ASSERT(result.second);
}
if (reinterpret_cast<uintptr_t>(address) + allocation.size
> reinterpret_cast<uintptr_t>(base) + size) {
ASSERT(allocationKind == pas_delegate_allocation);
auto result = allocations[heapKind].emplace(
reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base) + size),
Allocation(
(reinterpret_cast<uintptr_t>(address) + allocation.size)
- (reinterpret_cast<uintptr_t>(base) + size),
allocation.name,
allocationKind));
ASSERT(result.second);
}
}
void dumpStateForHeapKind(pas_heap_kind heapKind)
{
cout << pas_heap_kind_get_string(heapKind) << ":\n";
for (auto& pair : allocations[heapKind]) {
void* address = pair.first;
Allocation allocation = pair.second;
cout << " " << address << "..."
<< reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address)
+ allocation.size)
<< ": " << allocation.name << ", size = " << allocation.size
<< ", kind = " << pas_allocation_kind_get_string(allocation.allocationKind)
<< "\n";
}
}
void dumpStateHoldingLock()
{
dumpStateForHeapKind(pas_bootstrap_free_heap_kind);
dumpStateForHeapKind(pas_immortal_heap_kind);
dumpStateForHeapKind(pas_large_utility_free_heap_kind);
dumpStateForHeapKind(pas_utility_heap_kind);
}
void uninstall()
{
pas_allocation_callback = NULL;
pas_deallocation_callback = NULL;
}
} // anonymous namespace
extern "C" void pas_install_verifier()
{
pas_allocation_callback = allocationCallback;
pas_deallocation_callback = deallocationCallback;
atexit(uninstall);
}
extern "C" void pas_dump_state()
{
Locker locker;
dumpStateHoldingLock();
}