blob: a75f58dc146b3b6495ceb167adf4dabff06b0c89 [file] [log] [blame]
/*
* Copyright (C) 2017 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 "config.h"
#include "ProbeStack.h"
#include <memory>
#include <wtf/StdLibExtras.h>
#if ENABLE(MASM_PROBE)
namespace JSC {
namespace Probe {
static void* const maxLowWatermark = reinterpret_cast<void*>(std::numeric_limits<uintptr_t>::max());
#if ASAN_ENABLED
// FIXME: we should consider using the copy function for both ASan and non-ASan builds.
// https://bugs.webkit.org/show_bug.cgi?id=176961
SUPPRESS_ASAN
static void copyStackPage(void* dst, void* src, size_t size)
{
ASSERT(roundUpToMultipleOf<sizeof(uintptr_t)>(dst) == dst);
ASSERT(roundUpToMultipleOf<sizeof(uintptr_t)>(src) == src);
uintptr_t* dstPointer = reinterpret_cast<uintptr_t*>(dst);
uintptr_t* srcPointer = reinterpret_cast<uintptr_t*>(src);
for (; size; size -= sizeof(uintptr_t))
*dstPointer++ = *srcPointer++;
}
#else
#define copyStackPage(dst, src, size) std::memcpy(dst, src, size)
#endif
Page::Page(void* baseAddress)
: m_baseLogicalAddress(baseAddress)
, m_physicalAddressOffset(reinterpret_cast<uint8_t*>(&m_buffer) - reinterpret_cast<uint8_t*>(baseAddress))
{
copyStackPage(&m_buffer, baseAddress, s_pageSize);
}
void Page::flushWrites()
{
uint64_t dirtyBits = m_dirtyBits;
size_t offset = 0;
while (dirtyBits) {
// Find start.
if (dirtyBits & 1) {
size_t startOffset = offset;
// Find end.
do {
dirtyBits = dirtyBits >> 1;
offset += s_chunkSize;
} while (dirtyBits & 1);
size_t size = offset - startOffset;
uint8_t* src = reinterpret_cast<uint8_t*>(&m_buffer) + startOffset;
uint8_t* dst = reinterpret_cast<uint8_t*>(m_baseLogicalAddress) + startOffset;
copyStackPage(dst, src, size);
}
dirtyBits = dirtyBits >> 1;
offset += s_chunkSize;
}
m_dirtyBits = 0;
}
void* Page::lowWatermarkFromVisitingDirtyChunks()
{
uint64_t dirtyBits = m_dirtyBits;
size_t offset = 0;
while (dirtyBits) {
if (dirtyBits & 1)
return reinterpret_cast<uint8_t*>(m_baseLogicalAddress) + offset;
dirtyBits = dirtyBits >> 1;
offset += s_chunkSize;
}
return maxLowWatermark;
}
Stack::Stack(Stack&& other)
: m_stackBounds(WTFMove(other.m_stackBounds))
, m_pages(WTFMove(other.m_pages))
{
m_savedStackPointer = other.m_savedStackPointer;
#if !ASSERT_DISABLED
other.m_isValid = false;
#endif
}
bool Stack::hasWritesToFlush()
{
return std::any_of(m_pages.begin(), m_pages.end(), [] (auto& it) { return it.value->hasWritesToFlush(); });
}
void Stack::flushWrites()
{
for (auto it = m_pages.begin(); it != m_pages.end(); ++it)
it->value->flushWritesIfNeeded();
}
Page* Stack::ensurePageFor(void* address)
{
// Since the machine stack is always allocated in units of whole pages, asserting
// that the address is contained in the stack is sufficient to infer that its page
// is also contained in the stack.
RELEASE_ASSERT(m_stackBounds.contains(address));
// We may have gotten here because of a cache miss. So, look up the page first
// before allocating a new one,
void* baseAddress = Page::baseAddressFor(address);
auto it = m_pages.find(baseAddress);
if (LIKELY(it != m_pages.end()))
m_lastAccessedPage = it->value.get();
else {
std::unique_ptr<Page> page = std::make_unique<Page>(baseAddress);
auto result = m_pages.add(baseAddress, WTFMove(page));
m_lastAccessedPage = result.iterator->value.get();
}
m_lastAccessedPageBaseAddress = baseAddress;
return m_lastAccessedPage;
}
void* Stack::lowWatermarkFromVisitingDirtyPages()
{
void* low = maxLowWatermark;
for (auto it = m_pages.begin(); it != m_pages.end(); ++it) {
Page& page = *it->value;
if (!page.hasWritesToFlush() || low < page.baseAddress())
continue;
low = std::min(low, page.lowWatermarkFromVisitingDirtyChunks());
}
return low;
}
} // namespace Probe
} // namespace JSC
#endif // ENABLE(MASM_PROBE)