Add basic infrastructure for protecting V8's heap when leaving the VM
and unprotecting it when (re)entering. The functionality is enabled by the flag --protect-heap and requires V8 to be built with ENABLE_HEAP_PROTECTION and ENABLE_LOGGING_AND_PROFILING defined. Implemented on Linux and Windows but not yet for other platforms. Review URL: http://codereview.chromium.org/53004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1595 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1ba34bf86b
commit
bc3fb11881
@ -337,8 +337,20 @@ DEFINE_bool(log_regexp, false, "Log regular expression execution.")
|
|||||||
DEFINE_bool(sliding_state_window, false,
|
DEFINE_bool(sliding_state_window, false,
|
||||||
"Update sliding state window counters.")
|
"Update sliding state window counters.")
|
||||||
DEFINE_string(logfile, "v8.log", "Specify the name of the log file.")
|
DEFINE_string(logfile, "v8.log", "Specify the name of the log file.")
|
||||||
DEFINE_bool(oprofile, false,
|
DEFINE_bool(oprofile, false, "Enable JIT agent for OProfile.")
|
||||||
"Enable JIT agent for OProfile.")
|
|
||||||
|
//
|
||||||
|
// Heap protection flags
|
||||||
|
// Using heap protection requires ENABLE_LOGGING_AND_PROFILING as well.
|
||||||
|
//
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
#undef FLAG
|
||||||
|
#define FLAG FLAG_FULL
|
||||||
|
|
||||||
|
DEFINE_bool(protect_heap, false,
|
||||||
|
"Protect/unprotect V8's heap when leaving/entring the VM.")
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Disassembler only flags
|
// Disassembler only flags
|
||||||
|
@ -359,7 +359,8 @@ struct AccessorDescriptor {
|
|||||||
V(JS) \
|
V(JS) \
|
||||||
V(GC) \
|
V(GC) \
|
||||||
V(COMPILER) \
|
V(COMPILER) \
|
||||||
V(OTHER)
|
V(OTHER) \
|
||||||
|
V(EXTERNAL)
|
||||||
|
|
||||||
enum StateTag {
|
enum StateTag {
|
||||||
#define DEF_STATE_TAG(name) name,
|
#define DEF_STATE_TAG(name) name,
|
||||||
|
24
src/heap.cc
24
src/heap.cc
@ -2861,6 +2861,30 @@ void Heap::Shrink() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void Heap::Protect() {
|
||||||
|
new_space_.Protect();
|
||||||
|
map_space_->Protect();
|
||||||
|
old_pointer_space_->Protect();
|
||||||
|
old_data_space_->Protect();
|
||||||
|
code_space_->Protect();
|
||||||
|
lo_space_->Protect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Heap::Unprotect() {
|
||||||
|
new_space_.Unprotect();
|
||||||
|
map_space_->Unprotect();
|
||||||
|
old_pointer_space_->Unprotect();
|
||||||
|
old_data_space_->Unprotect();
|
||||||
|
code_space_->Unprotect();
|
||||||
|
lo_space_->Unprotect();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
class PrintHandleVisitor: public ObjectVisitor {
|
class PrintHandleVisitor: public ObjectVisitor {
|
||||||
|
@ -273,6 +273,12 @@ class Heap : public AllStatic {
|
|||||||
return new_space_.allocation_limit_address();
|
return new_space_.allocation_limit_address();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect the heap by marking all spaces read-only/writable.
|
||||||
|
static void Protect();
|
||||||
|
static void Unprotect();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Allocates and initializes a new JavaScript object based on a
|
// Allocates and initializes a new JavaScript object based on a
|
||||||
// constructor.
|
// constructor.
|
||||||
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
|
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
|
||||||
|
35
src/log.cc
35
src/log.cc
@ -29,10 +29,12 @@
|
|||||||
|
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
|
#include "bootstrapper.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "platform.h"
|
|
||||||
#include "string-stream.h"
|
|
||||||
#include "macro-assembler.h"
|
#include "macro-assembler.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "serialize.h"
|
||||||
|
#include "string-stream.h"
|
||||||
|
|
||||||
namespace v8 { namespace internal {
|
namespace v8 { namespace internal {
|
||||||
|
|
||||||
@ -1115,10 +1117,23 @@ VMState::VMState(StateTag state) {
|
|||||||
|
|
||||||
if (FLAG_log_state_changes) {
|
if (FLAG_log_state_changes) {
|
||||||
LOG(UncheckedStringEvent("Entering", StateToString(state_)));
|
LOG(UncheckedStringEvent("Entering", StateToString(state_)));
|
||||||
if (previous_) {
|
if (previous_ != NULL) {
|
||||||
LOG(UncheckedStringEvent("From", StateToString(previous_->state_)));
|
LOG(UncheckedStringEvent("From", StateToString(previous_->state_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
if (FLAG_protect_heap && previous_ != NULL) {
|
||||||
|
if (state_ == EXTERNAL) {
|
||||||
|
// We are leaving V8.
|
||||||
|
ASSERT(previous_ == NULL || previous_->state_ != EXTERNAL);
|
||||||
|
Heap::Protect();
|
||||||
|
} else {
|
||||||
|
// Are we entering V8?
|
||||||
|
if (previous_->state_ == EXTERNAL) Heap::Unprotect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1127,10 +1142,22 @@ VMState::~VMState() {
|
|||||||
|
|
||||||
if (FLAG_log_state_changes) {
|
if (FLAG_log_state_changes) {
|
||||||
LOG(UncheckedStringEvent("Leaving", StateToString(state_)));
|
LOG(UncheckedStringEvent("Leaving", StateToString(state_)));
|
||||||
if (previous_) {
|
if (previous_ != NULL) {
|
||||||
LOG(UncheckedStringEvent("To", StateToString(previous_->state_)));
|
LOG(UncheckedStringEvent("To", StateToString(previous_->state_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
if (FLAG_protect_heap && previous_ != NULL) {
|
||||||
|
if (state_ == EXTERNAL) {
|
||||||
|
// Are we (re)entering V8?
|
||||||
|
if (previous_->state_ != EXTERNAL) Heap::Unprotect();
|
||||||
|
} else {
|
||||||
|
// Are we leaving V8?
|
||||||
|
if (previous_->state_ == EXTERNAL) Heap::Protect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -257,6 +257,20 @@ void OS::Free(void* buf, const size_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void OS::Protect(void* address, size_t size) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void OS::Sleep(int milliseconds) {
|
void OS::Sleep(int milliseconds) {
|
||||||
unsigned int ms = static_cast<unsigned int>(milliseconds);
|
unsigned int ms = static_cast<unsigned int>(milliseconds);
|
||||||
usleep(1000 * ms);
|
usleep(1000 * ms);
|
||||||
|
@ -234,9 +234,9 @@ size_t OS::AllocateAlignment() {
|
|||||||
|
|
||||||
void* OS::Allocate(const size_t requested,
|
void* OS::Allocate(const size_t requested,
|
||||||
size_t* allocated,
|
size_t* allocated,
|
||||||
bool executable) {
|
bool is_executable) {
|
||||||
const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
|
const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
|
||||||
int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
|
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
||||||
void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
if (mbase == MAP_FAILED) {
|
if (mbase == MAP_FAILED) {
|
||||||
LOG(StringEvent("OS::Allocate", "mmap failed"));
|
LOG(StringEvent("OS::Allocate", "mmap failed"));
|
||||||
@ -248,12 +248,29 @@ void* OS::Allocate(const size_t requested,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OS::Free(void* buf, const size_t length) {
|
void OS::Free(void* address, const size_t size) {
|
||||||
// TODO(1240712): munmap has a return value which is ignored here.
|
// TODO(1240712): munmap has a return value which is ignored here.
|
||||||
munmap(buf, length);
|
munmap(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void OS::Protect(void* address, size_t size) {
|
||||||
|
// TODO(1240712): mprotect has a return value which is ignored here.
|
||||||
|
mprotect(address, size, PROT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
||||||
|
// TODO(1240712): mprotect has a return value which is ignored here.
|
||||||
|
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
||||||
|
mprotect(address, size, prot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void OS::Sleep(int milliseconds) {
|
void OS::Sleep(int milliseconds) {
|
||||||
unsigned int ms = static_cast<unsigned int>(milliseconds);
|
unsigned int ms = static_cast<unsigned int>(milliseconds);
|
||||||
usleep(1000 * ms);
|
usleep(1000 * ms);
|
||||||
@ -267,7 +284,7 @@ void OS::Abort() {
|
|||||||
|
|
||||||
|
|
||||||
void OS::DebugBreak() {
|
void OS::DebugBreak() {
|
||||||
#if defined (__arm__) || defined(__thumb__)
|
#ifdef ARM
|
||||||
asm("bkpt 0");
|
asm("bkpt 0");
|
||||||
#else
|
#else
|
||||||
asm("int $3");
|
asm("int $3");
|
||||||
@ -418,8 +435,8 @@ bool VirtualMemory::IsReserved() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
|
bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
|
||||||
int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
|
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
||||||
if (MAP_FAILED == mmap(address, size, prot,
|
if (MAP_FAILED == mmap(address, size, prot,
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
|
||||||
kMmapFd, kMmapFdOffset)) {
|
kMmapFd, kMmapFdOffset)) {
|
||||||
|
@ -228,9 +228,9 @@ size_t OS::AllocateAlignment() {
|
|||||||
|
|
||||||
void* OS::Allocate(const size_t requested,
|
void* OS::Allocate(const size_t requested,
|
||||||
size_t* allocated,
|
size_t* allocated,
|
||||||
bool executable) {
|
bool is_executable) {
|
||||||
const size_t msize = RoundUp(requested, getpagesize());
|
const size_t msize = RoundUp(requested, getpagesize());
|
||||||
int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
|
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
||||||
void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
|
void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||||
if (mbase == MAP_FAILED) {
|
if (mbase == MAP_FAILED) {
|
||||||
LOG(StringEvent("OS::Allocate", "mmap failed"));
|
LOG(StringEvent("OS::Allocate", "mmap failed"));
|
||||||
@ -242,12 +242,26 @@ void* OS::Allocate(const size_t requested,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OS::Free(void* buf, const size_t length) {
|
void OS::Free(void* address, const size_t size) {
|
||||||
// TODO(1240712): munmap has a return value which is ignored here.
|
// TODO(1240712): munmap has a return value which is ignored here.
|
||||||
munmap(buf, length);
|
munmap(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void OS::Protect(void* address, size_t size) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void OS::Sleep(int milliseconds) {
|
void OS::Sleep(int milliseconds) {
|
||||||
usleep(1000 * milliseconds);
|
usleep(1000 * milliseconds);
|
||||||
}
|
}
|
||||||
@ -370,8 +384,8 @@ bool VirtualMemory::IsReserved() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
|
bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
|
||||||
int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
|
int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
|
||||||
if (MAP_FAILED == mmap(address, size, prot,
|
if (MAP_FAILED == mmap(address, size, prot,
|
||||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
|
||||||
kMmapFd, kMmapFdOffset)) {
|
kMmapFd, kMmapFdOffset)) {
|
||||||
@ -389,6 +403,7 @@ bool VirtualMemory::Uncommit(void* address, size_t size) {
|
|||||||
kMmapFd, kMmapFdOffset) != MAP_FAILED;
|
kMmapFd, kMmapFdOffset) != MAP_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ThreadHandle::PlatformData : public Malloced {
|
class ThreadHandle::PlatformData : public Malloced {
|
||||||
public:
|
public:
|
||||||
explicit PlatformData(ThreadHandle::Kind kind) {
|
explicit PlatformData(ThreadHandle::Kind kind) {
|
||||||
|
@ -173,6 +173,20 @@ void OS::Free(void* buf, const size_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void OS::Protect(void* address, size_t size) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void OS::Sleep(int milliseconds) {
|
void OS::Sleep(int milliseconds) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
@ -801,12 +801,12 @@ size_t OS::AllocateAlignment() {
|
|||||||
|
|
||||||
void* OS::Allocate(const size_t requested,
|
void* OS::Allocate(const size_t requested,
|
||||||
size_t* allocated,
|
size_t* allocated,
|
||||||
bool executable) {
|
bool is_executable) {
|
||||||
// VirtualAlloc rounds allocated size to page size automatically.
|
// VirtualAlloc rounds allocated size to page size automatically.
|
||||||
size_t msize = RoundUp(requested, GetPageSize());
|
size_t msize = RoundUp(requested, GetPageSize());
|
||||||
|
|
||||||
// Windows XP SP2 allows Data Excution Prevention (DEP).
|
// Windows XP SP2 allows Data Excution Prevention (DEP).
|
||||||
int prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||||
LPVOID mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot);
|
LPVOID mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot);
|
||||||
if (mbase == NULL) {
|
if (mbase == NULL) {
|
||||||
LOG(StringEvent("OS::Allocate", "VirtualAlloc failed"));
|
LOG(StringEvent("OS::Allocate", "VirtualAlloc failed"));
|
||||||
@ -821,13 +821,32 @@ void* OS::Allocate(const size_t requested,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OS::Free(void* buf, const size_t length) {
|
void OS::Free(void* address, const size_t size) {
|
||||||
// TODO(1240712): VirtualFree has a return value which is ignored here.
|
// TODO(1240712): VirtualFree has a return value which is ignored here.
|
||||||
VirtualFree(buf, 0, MEM_RELEASE);
|
VirtualFree(address, 0, MEM_RELEASE);
|
||||||
USE(length);
|
USE(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void OS::Protect(void* address, size_t size) {
|
||||||
|
// TODO(1240712): VirtualProtect has a return value which is ignored here.
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(address, size, PAGE_READONLY, &old_protect);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OS::Unprotect(void* address, size_t size, bool is_executable) {
|
||||||
|
// TODO(1240712): VirtualProtect has a return value which is ignored here.
|
||||||
|
DWORD new_protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(address, size, new_protect, &old_protect);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void OS::Sleep(int milliseconds) {
|
void OS::Sleep(int milliseconds) {
|
||||||
::Sleep(milliseconds);
|
::Sleep(milliseconds);
|
||||||
}
|
}
|
||||||
@ -1299,8 +1318,8 @@ VirtualMemory::~VirtualMemory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
|
bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
|
||||||
int prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||||
if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) {
|
if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -168,11 +168,17 @@ class OS {
|
|||||||
// Returns the address of allocated memory, or NULL if failed.
|
// Returns the address of allocated memory, or NULL if failed.
|
||||||
static void* Allocate(const size_t requested,
|
static void* Allocate(const size_t requested,
|
||||||
size_t* allocated,
|
size_t* allocated,
|
||||||
bool executable);
|
bool is_executable);
|
||||||
static void Free(void* buf, const size_t length);
|
static void Free(void* address, const size_t size);
|
||||||
// Get the Alignment guaranteed by Allocate().
|
// Get the Alignment guaranteed by Allocate().
|
||||||
static size_t AllocateAlignment();
|
static size_t AllocateAlignment();
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect a block of memory by marking it read-only/writable.
|
||||||
|
static void Protect(void* address, size_t size);
|
||||||
|
static void Unprotect(void* address, size_t size, bool is_executable);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Returns an indication of whether a pointer is in a space that
|
// Returns an indication of whether a pointer is in a space that
|
||||||
// has been allocated by Allocate(). This method may conservatively
|
// has been allocated by Allocate(). This method may conservatively
|
||||||
// always return false, but giving more accurate information may
|
// always return false, but giving more accurate information may
|
||||||
@ -267,7 +273,7 @@ class VirtualMemory {
|
|||||||
size_t size() { return size_; }
|
size_t size() { return size_; }
|
||||||
|
|
||||||
// Commits real memory. Returns whether the operation succeeded.
|
// Commits real memory. Returns whether the operation succeeded.
|
||||||
bool Commit(void* address, size_t size, bool executable);
|
bool Commit(void* address, size_t size, bool is_executable);
|
||||||
|
|
||||||
// Uncommit real memory. Returns whether the operation succeeded.
|
// Uncommit real memory. Returns whether the operation succeeded.
|
||||||
bool Uncommit(void* address, size_t size);
|
bool Uncommit(void* address, size_t size);
|
||||||
|
@ -219,6 +219,43 @@ PagedSpace* MemoryAllocator::PageOwner(Page* page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MemoryAllocator::InInitialChunk(Address address) {
|
||||||
|
if (initial_chunk_ == NULL) return false;
|
||||||
|
|
||||||
|
Address start = static_cast<Address>(initial_chunk_->address());
|
||||||
|
return (start <= address) && (address < start + initial_chunk_->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void MemoryAllocator::Protect(Address start, size_t size) {
|
||||||
|
OS::Protect(start, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryAllocator::Unprotect(Address start,
|
||||||
|
size_t size,
|
||||||
|
Executability executable) {
|
||||||
|
OS::Unprotect(start, size, executable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryAllocator::ProtectChunkFromPage(Page* page) {
|
||||||
|
int id = GetChunkId(page);
|
||||||
|
OS::Protect(chunks_[id].address(), chunks_[id].size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryAllocator::UnprotectChunkFromPage(Page* page) {
|
||||||
|
int id = GetChunkId(page);
|
||||||
|
OS::Unprotect(chunks_[id].address(), chunks_[id].size(),
|
||||||
|
chunks_[id].owner()->executable() == EXECUTABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// PagedSpace
|
// PagedSpace
|
||||||
|
|
||||||
|
@ -302,9 +302,8 @@ Page* MemoryAllocator::CommitPages(Address start, size_t size,
|
|||||||
*num_pages = PagesInChunk(start, size);
|
*num_pages = PagesInChunk(start, size);
|
||||||
ASSERT(*num_pages > 0);
|
ASSERT(*num_pages > 0);
|
||||||
ASSERT(initial_chunk_ != NULL);
|
ASSERT(initial_chunk_ != NULL);
|
||||||
ASSERT(initial_chunk_->address() <= start);
|
ASSERT(InInitialChunk(start));
|
||||||
ASSERT(start + size <= reinterpret_cast<Address>(initial_chunk_->address())
|
ASSERT(InInitialChunk(start + size - 1));
|
||||||
+ initial_chunk_->size());
|
|
||||||
if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) {
|
if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) {
|
||||||
return Page::FromAddress(NULL);
|
return Page::FromAddress(NULL);
|
||||||
}
|
}
|
||||||
@ -325,9 +324,8 @@ bool MemoryAllocator::CommitBlock(Address start,
|
|||||||
ASSERT(start != NULL);
|
ASSERT(start != NULL);
|
||||||
ASSERT(size > 0);
|
ASSERT(size > 0);
|
||||||
ASSERT(initial_chunk_ != NULL);
|
ASSERT(initial_chunk_ != NULL);
|
||||||
ASSERT(initial_chunk_->address() <= start);
|
ASSERT(InInitialChunk(start));
|
||||||
ASSERT(start + size <= reinterpret_cast<Address>(initial_chunk_->address())
|
ASSERT(InInitialChunk(start + size - 1));
|
||||||
+ initial_chunk_->size());
|
|
||||||
|
|
||||||
if (!initial_chunk_->Commit(start, size, executable)) return false;
|
if (!initial_chunk_->Commit(start, size, executable)) return false;
|
||||||
Counters::memory_allocated.Increment(size);
|
Counters::memory_allocated.Increment(size);
|
||||||
@ -407,14 +405,7 @@ void MemoryAllocator::DeleteChunk(int chunk_id) {
|
|||||||
// We cannot free a chunk contained in the initial chunk because it was not
|
// We cannot free a chunk contained in the initial chunk because it was not
|
||||||
// allocated with AllocateRawMemory. Instead we uncommit the virtual
|
// allocated with AllocateRawMemory. Instead we uncommit the virtual
|
||||||
// memory.
|
// memory.
|
||||||
bool in_initial_chunk = false;
|
if (InInitialChunk(c.address())) {
|
||||||
if (initial_chunk_ != NULL) {
|
|
||||||
Address start = static_cast<Address>(initial_chunk_->address());
|
|
||||||
Address end = start + initial_chunk_->size();
|
|
||||||
in_initial_chunk = (start <= c.address()) && (c.address() < end);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_initial_chunk) {
|
|
||||||
// TODO(1240712): VirtualMemory::Uncommit has a return value which
|
// TODO(1240712): VirtualMemory::Uncommit has a return value which
|
||||||
// is ignored here.
|
// is ignored here.
|
||||||
initial_chunk_->Uncommit(c.address(), c.size());
|
initial_chunk_->Uncommit(c.address(), c.size());
|
||||||
@ -529,6 +520,28 @@ void PagedSpace::TearDown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void PagedSpace::Protect() {
|
||||||
|
Page* page = first_page_;
|
||||||
|
while (page->is_valid()) {
|
||||||
|
MemoryAllocator::ProtectChunkFromPage(page);
|
||||||
|
page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PagedSpace::Unprotect() {
|
||||||
|
Page* page = first_page_;
|
||||||
|
while (page->is_valid()) {
|
||||||
|
MemoryAllocator::UnprotectChunkFromPage(page);
|
||||||
|
page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void PagedSpace::ClearRSet() {
|
void PagedSpace::ClearRSet() {
|
||||||
PageIterator it(this, PageIterator::ALL_PAGES);
|
PageIterator it(this, PageIterator::ALL_PAGES);
|
||||||
while (it.has_next()) {
|
while (it.has_next()) {
|
||||||
@ -834,6 +847,24 @@ void NewSpace::TearDown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void NewSpace::Protect() {
|
||||||
|
MemoryAllocator::Protect(ToSpaceLow(), Capacity());
|
||||||
|
MemoryAllocator::Protect(FromSpaceLow(), Capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void NewSpace::Unprotect() {
|
||||||
|
MemoryAllocator::Unprotect(ToSpaceLow(), Capacity(),
|
||||||
|
to_space_.executable());
|
||||||
|
MemoryAllocator::Unprotect(FromSpaceLow(), Capacity(),
|
||||||
|
from_space_.executable());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void NewSpace::Flip() {
|
void NewSpace::Flip() {
|
||||||
SemiSpace tmp = from_space_;
|
SemiSpace tmp = from_space_;
|
||||||
from_space_ = to_space_;
|
from_space_ = to_space_;
|
||||||
@ -2242,6 +2273,30 @@ void LargeObjectSpace::TearDown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
|
||||||
|
void LargeObjectSpace::Protect() {
|
||||||
|
LargeObjectChunk* chunk = first_chunk_;
|
||||||
|
while (chunk != NULL) {
|
||||||
|
MemoryAllocator::Protect(chunk->address(), chunk->size());
|
||||||
|
chunk = chunk->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LargeObjectSpace::Unprotect() {
|
||||||
|
LargeObjectChunk* chunk = first_chunk_;
|
||||||
|
while (chunk != NULL) {
|
||||||
|
bool is_code = chunk->GetObject()->IsCode();
|
||||||
|
MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
|
||||||
|
is_code ? EXECUTABLE : NOT_EXECUTABLE);
|
||||||
|
chunk = chunk->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
|
Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
|
||||||
int object_size,
|
int object_size,
|
||||||
Executability executable) {
|
Executability executable) {
|
||||||
|
44
src/spaces.h
44
src/spaces.h
@ -277,16 +277,22 @@ class Space : public Malloced {
|
|||||||
public:
|
public:
|
||||||
Space(AllocationSpace id, Executability executable)
|
Space(AllocationSpace id, Executability executable)
|
||||||
: id_(id), executable_(executable) {}
|
: id_(id), executable_(executable) {}
|
||||||
|
|
||||||
virtual ~Space() {}
|
virtual ~Space() {}
|
||||||
|
|
||||||
// Does the space need executable memory?
|
// Does the space need executable memory?
|
||||||
Executability executable() { return executable_; }
|
Executability executable() { return executable_; }
|
||||||
|
|
||||||
// Identity used in error reporting.
|
// Identity used in error reporting.
|
||||||
AllocationSpace identity() { return id_; }
|
AllocationSpace identity() { return id_; }
|
||||||
|
|
||||||
virtual int Size() = 0;
|
virtual int Size() = 0;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
virtual void Verify() = 0;
|
virtual void Verify() = 0;
|
||||||
virtual void Print() = 0;
|
virtual void Print() = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AllocationSpace id_;
|
AllocationSpace id_;
|
||||||
Executability executable_;
|
Executability executable_;
|
||||||
@ -396,6 +402,17 @@ class MemoryAllocator : public AllStatic {
|
|||||||
static Page* FindFirstPageInSameChunk(Page* p);
|
static Page* FindFirstPageInSameChunk(Page* p);
|
||||||
static Page* FindLastPageInSameChunk(Page* p);
|
static Page* FindLastPageInSameChunk(Page* p);
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect a block of memory by marking it read-only/writable.
|
||||||
|
static inline void Protect(Address start, size_t size);
|
||||||
|
static inline void Unprotect(Address start, size_t size,
|
||||||
|
Executability executable);
|
||||||
|
|
||||||
|
// Protect/unprotect a chunk given a page in the chunk.
|
||||||
|
static inline void ProtectChunkFromPage(Page* page);
|
||||||
|
static inline void UnprotectChunkFromPage(Page* page);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Reports statistic info of the space.
|
// Reports statistic info of the space.
|
||||||
static void ReportStatistics();
|
static void ReportStatistics();
|
||||||
@ -460,6 +477,9 @@ class MemoryAllocator : public AllStatic {
|
|||||||
// Returns the chunk id that a page belongs to.
|
// Returns the chunk id that a page belongs to.
|
||||||
static inline int GetChunkId(Page* p);
|
static inline int GetChunkId(Page* p);
|
||||||
|
|
||||||
|
// True if the address lies in the initial chunk.
|
||||||
|
static inline bool InInitialChunk(Address address);
|
||||||
|
|
||||||
// Initializes pages in a chunk. Returns the first page address.
|
// Initializes pages in a chunk. Returns the first page address.
|
||||||
// This function and GetChunkId() are provided for the mark-compact
|
// This function and GetChunkId() are provided for the mark-compact
|
||||||
// collector to rebuild page headers in the from space, which is
|
// collector to rebuild page headers in the from space, which is
|
||||||
@ -669,7 +689,6 @@ class AllocationStats BASE_EMBEDDED {
|
|||||||
|
|
||||||
|
|
||||||
class PagedSpace : public Space {
|
class PagedSpace : public Space {
|
||||||
friend class PageIterator;
|
|
||||||
public:
|
public:
|
||||||
// Creates a space with a maximum capacity, and an id.
|
// Creates a space with a maximum capacity, and an id.
|
||||||
PagedSpace(int max_capacity, AllocationSpace id, Executability executable);
|
PagedSpace(int max_capacity, AllocationSpace id, Executability executable);
|
||||||
@ -764,6 +783,12 @@ class PagedSpace : public Space {
|
|||||||
// Ensures that the capacity is at least 'capacity'. Returns false on failure.
|
// Ensures that the capacity is at least 'capacity'. Returns false on failure.
|
||||||
bool EnsureCapacity(int capacity);
|
bool EnsureCapacity(int capacity);
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect the space by marking it read-only/writable.
|
||||||
|
void Protect();
|
||||||
|
void Unprotect();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Print meta info and objects in this space.
|
// Print meta info and objects in this space.
|
||||||
virtual void Print();
|
virtual void Print();
|
||||||
@ -834,6 +859,8 @@ class PagedSpace : public Space {
|
|||||||
// Returns the number of total pages in this space.
|
// Returns the number of total pages in this space.
|
||||||
int CountTotalPages();
|
int CountTotalPages();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
friend class PageIterator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -1117,6 +1144,12 @@ class NewSpace : public Space {
|
|||||||
bool ToSpaceContains(Address a) { return to_space_.Contains(a); }
|
bool ToSpaceContains(Address a) { return to_space_.Contains(a); }
|
||||||
bool FromSpaceContains(Address a) { return from_space_.Contains(a); }
|
bool FromSpaceContains(Address a) { return from_space_.Contains(a); }
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect the space by marking it read-only/writable.
|
||||||
|
virtual void Protect();
|
||||||
|
virtual void Unprotect();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Verify the active semispace.
|
// Verify the active semispace.
|
||||||
virtual void Verify();
|
virtual void Verify();
|
||||||
@ -1554,7 +1587,6 @@ class LargeObjectChunk {
|
|||||||
|
|
||||||
|
|
||||||
class LargeObjectSpace : public Space {
|
class LargeObjectSpace : public Space {
|
||||||
friend class LargeObjectIterator;
|
|
||||||
public:
|
public:
|
||||||
explicit LargeObjectSpace(AllocationSpace id);
|
explicit LargeObjectSpace(AllocationSpace id);
|
||||||
virtual ~LargeObjectSpace() {}
|
virtual ~LargeObjectSpace() {}
|
||||||
@ -1606,6 +1638,12 @@ class LargeObjectSpace : public Space {
|
|||||||
// Checks whether the space is empty.
|
// Checks whether the space is empty.
|
||||||
bool IsEmpty() { return first_chunk_ == NULL; }
|
bool IsEmpty() { return first_chunk_ == NULL; }
|
||||||
|
|
||||||
|
#ifdef ENABLE_HEAP_PROTECTION
|
||||||
|
// Protect/unprotect the space by marking it read-only/writable.
|
||||||
|
void Protect();
|
||||||
|
void Unprotect();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
virtual void Verify();
|
virtual void Verify();
|
||||||
virtual void Print();
|
virtual void Print();
|
||||||
@ -1635,6 +1673,8 @@ class LargeObjectSpace : public Space {
|
|||||||
// required for extra_object_bytes of extra pointers (in bytes).
|
// required for extra_object_bytes of extra pointers (in bytes).
|
||||||
static inline int ExtraRSetBytesFor(int extra_object_bytes);
|
static inline int ExtraRSetBytesFor(int extra_object_bytes);
|
||||||
|
|
||||||
|
friend class LargeObjectIterator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TRACK_MEMORY("LargeObjectSpace")
|
TRACK_MEMORY("LargeObjectSpace")
|
||||||
};
|
};
|
||||||
|
@ -163,7 +163,7 @@ class SimpleProgressIndicator(ProgressIndicator):
|
|||||||
print failed.output.stdout.strip()
|
print failed.output.stdout.strip()
|
||||||
print "Command: %s" % EscapeCommand(failed.command)
|
print "Command: %s" % EscapeCommand(failed.command)
|
||||||
if failed.HasCrashed():
|
if failed.HasCrashed():
|
||||||
print "--- CRASHED ---"
|
print "--- CRASHED ---"
|
||||||
if len(self.failed) == 0:
|
if len(self.failed) == 0:
|
||||||
print "==="
|
print "==="
|
||||||
print "=== All tests succeeded"
|
print "=== All tests succeeded"
|
||||||
@ -244,7 +244,7 @@ class CompactProgressIndicator(ProgressIndicator):
|
|||||||
print self.templates['stderr'] % stderr
|
print self.templates['stderr'] % stderr
|
||||||
print "Command: %s" % EscapeCommand(output.command)
|
print "Command: %s" % EscapeCommand(output.command)
|
||||||
if output.HasCrashed():
|
if output.HasCrashed():
|
||||||
print "--- CRASHED ---"
|
print "--- CRASHED ---"
|
||||||
|
|
||||||
def Truncate(self, str, length):
|
def Truncate(self, str, length):
|
||||||
if length and (len(str) > (length - 3)):
|
if length and (len(str) > (length - 3)):
|
||||||
@ -345,7 +345,7 @@ class TestCase(object):
|
|||||||
|
|
||||||
def GetSource(self):
|
def GetSource(self):
|
||||||
return "(no source available)"
|
return "(no source available)"
|
||||||
|
|
||||||
def RunCommand(self, command):
|
def RunCommand(self, command):
|
||||||
full_command = self.context.processor(command)
|
full_command = self.context.processor(command)
|
||||||
output = Execute(full_command, self.context, self.context.timeout)
|
output = Execute(full_command, self.context, self.context.timeout)
|
||||||
@ -411,7 +411,7 @@ def Win32SetErrorMode(mode):
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
return prev_error_mode
|
return prev_error_mode
|
||||||
|
|
||||||
def RunProcess(context, timeout, args, **rest):
|
def RunProcess(context, timeout, args, **rest):
|
||||||
if context.verbose: print "#", " ".join(args)
|
if context.verbose: print "#", " ".join(args)
|
||||||
popen_args = args
|
popen_args = args
|
||||||
|
Loading…
Reference in New Issue
Block a user