blob: 94379aa71692505ddc0cfdd4a82680c1f28216cf [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef StackFrameDepth_h
#define StackFrameDepth_h
#include "platform/PlatformExport.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include <cstddef>
#include <stdint.h>
namespace blink {
// StackFrameDepth keeps track of current call stack frame depth.
// It is specifically used to control stack usage while tracing
// the object graph during a GC.
//
// Use isSafeToRecurse() to determine if it is safe to consume
// more stack by invoking another recursive call.
class PLATFORM_EXPORT StackFrameDepth final {
STATIC_ONLY(StackFrameDepth);
public:
inline static bool isSafeToRecurse()
{
// Asssume that the stack grows towards lower addresses, which
// all the ABIs currently supported do.
//
// A unit test checks that the assumption holds for a target
// (HeapTest.StackGrowthDirection.)
return currentStackFrame() > s_stackFrameLimit;
}
static void enableStackLimit();
static void disableStackLimit()
{
s_stackFrameLimit = kMinimumStackLimit;
}
#if ENABLE(ASSERT)
inline static bool isEnabled() { return s_stackFrameLimit != kMinimumStackLimit; }
inline static bool isAcceptableStackUse()
{
#if defined(ADDRESS_SANITIZER)
// ASan adds extra stack usage leading to too noisy asserts.
return true;
#else
return !isEnabled() || isSafeToRecurse();
#endif
}
#endif
static size_t getUnderestimatedStackSize();
static void* getStackStart();
#if COMPILER(MSVC)
// Ignore C4172: returning address of local variable or temporary: dummy. This
// warning suppression has to go outside of the function to take effect.
#pragma warning(push)
#pragma warning(disable: 4172)
#endif
static uintptr_t currentStackFrame(const char* dummy = nullptr)
{
#if COMPILER(GCC)
return reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
#elif COMPILER(MSVC)
return reinterpret_cast<uintptr_t>(&dummy) - sizeof(void*);
#else
#error "Stack frame pointer estimation not supported on this platform."
return 0;
#endif
}
#if COMPILER(MSVC)
#pragma warning(pop)
#endif
private:
// The maximum depth of eager, unrolled trace() calls that is
// considered safe and allowed for targets with an unknown
// thread stack size.
static const int kSafeStackFrameSize = 32 * 1024;
// The stack pointer is assumed to grow towards lower addresses;
// |kMinimumStackLimit| then being the limit that a stack
// pointer will always exceed.
static const uintptr_t kMinimumStackLimit = ~0ul;
static uintptr_t getFallbackStackLimit();
// The (pointer-valued) stack limit.
static uintptr_t s_stackFrameLimit;
};
class StackFrameDepthScope {
STACK_ALLOCATED();
WTF_MAKE_NONCOPYABLE(StackFrameDepthScope);
public:
StackFrameDepthScope()
{
StackFrameDepth::enableStackLimit();
// Enabled unless under stack pressure.
ASSERT(StackFrameDepth::isSafeToRecurse() || !StackFrameDepth::isEnabled());
}
~StackFrameDepthScope()
{
StackFrameDepth::disableStackLimit();
}
};
} // namespace blink
#endif // StackFrameDepth_h