mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 15:39:54 +00:00
Reintroducing MallocHooks changes with fix for infinite loop in MallocHooks on Platform::Exit.
BUG= R=zra@google.com Review-Url: https://codereview.chromium.org/2643303003 .
This commit is contained in:
parent
a7f7a311c0
commit
7bf5d87017
|
@ -2,6 +2,8 @@
|
|||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import("runtime_args.gni")
|
||||
|
||||
declare_args() {
|
||||
# Instead of using is_debug, we introduce a different flag for specifying a
|
||||
# Debug build of Dart so that clients can still use a Release build of Dart
|
||||
|
@ -146,6 +148,12 @@ config("dart_config") {
|
|||
defines += [ "NDEBUG" ]
|
||||
}
|
||||
|
||||
include_dirs = []
|
||||
if (dart_use_tcmalloc) {
|
||||
defines += [ "DART_USE_TCMALLOC" ]
|
||||
include_dirs += [ "../third_party/tcmalloc/gperftools/src" ]
|
||||
}
|
||||
|
||||
if (!is_win) {
|
||||
cflags = [
|
||||
"-Werror",
|
||||
|
|
|
@ -297,6 +297,10 @@ executable("gen_snapshot") {
|
|||
|
||||
include_dirs = [ ".." ]
|
||||
|
||||
if (dart_use_tcmalloc) {
|
||||
deps += [ "//third_party/tcmalloc" ]
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
libs = [
|
||||
"CoreFoundation.framework",
|
||||
|
@ -574,7 +578,6 @@ template("dart_executable") {
|
|||
|
||||
if (dart_use_tcmalloc) {
|
||||
deps += [ "//third_party/tcmalloc" ]
|
||||
defines += [ "DART_USE_TCMALLOC" ]
|
||||
}
|
||||
|
||||
sources = [
|
||||
|
|
|
@ -1151,9 +1151,6 @@
|
|||
'dependencies': [
|
||||
'../third_party/tcmalloc/tcmalloc.gypi:tcmalloc',
|
||||
],
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
],
|
||||
'configurations': {
|
||||
|
@ -1348,9 +1345,6 @@
|
|||
'dependencies': [
|
||||
'../third_party/tcmalloc/tcmalloc.gypi:tcmalloc',
|
||||
],
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
],
|
||||
'configurations': {
|
||||
|
|
|
@ -383,6 +383,7 @@ static void WriteSnapshotFile(const char* filename,
|
|||
Log::PrintErr("Error: Unable to write snapshot file: %s\n\n", filename);
|
||||
Dart_ExitScope();
|
||||
Dart_ShutdownIsolate();
|
||||
Dart_Cleanup();
|
||||
exit(kErrorExitCode);
|
||||
}
|
||||
if (!file->WriteFully(buffer, size)) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "vm/heap.h"
|
||||
#include "vm/isolate.h"
|
||||
#include "vm/kernel_isolate.h"
|
||||
#include "vm/malloc_hooks.h"
|
||||
#include "vm/message_handler.h"
|
||||
#include "vm/metrics.h"
|
||||
#include "vm/object.h"
|
||||
|
@ -154,6 +155,7 @@ char* Dart::InitOnce(const uint8_t* vm_isolate_snapshot,
|
|||
start_time_micros_ = OS::GetCurrentMonotonicMicros();
|
||||
VirtualMemory::InitOnce();
|
||||
OSThread::InitOnce();
|
||||
MallocHooks::InitOnce();
|
||||
if (FLAG_support_timeline) {
|
||||
Timeline::InitOnce();
|
||||
}
|
||||
|
@ -491,7 +493,7 @@ const char* Dart::Cleanup() {
|
|||
if (FLAG_trace_shutdown) {
|
||||
OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done\n", UptimeMillis());
|
||||
}
|
||||
|
||||
MallocHooks::TearDown();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ class BaseDirectChainedHashMap : public B {
|
|||
}
|
||||
|
||||
void Insert(typename KeyValueTrait::Pair kv);
|
||||
bool Remove(typename KeyValueTrait::Key key);
|
||||
|
||||
typename KeyValueTrait::Value LookupValue(
|
||||
typename KeyValueTrait::Key key) const;
|
||||
|
@ -308,6 +309,68 @@ void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Insert(
|
|||
}
|
||||
|
||||
|
||||
template <typename KeyValueTrait, typename B, typename Allocator>
|
||||
bool BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Remove(
|
||||
typename KeyValueTrait::Key key) {
|
||||
uword pos = Bound(static_cast<uword>(KeyValueTrait::Hashcode(key)));
|
||||
|
||||
// Check to see if the first element in the bucket is the one we want to
|
||||
// remove.
|
||||
if (KeyValueTrait::KeyOf(array_[pos].kv) == key) {
|
||||
if (array_[pos].next == kNil) {
|
||||
array_[pos] = HashMapListElement();
|
||||
} else {
|
||||
intptr_t next = array_[pos].next;
|
||||
array_[pos] = lists_[next];
|
||||
lists_[next] = HashMapListElement();
|
||||
lists_[next].next = free_list_head_;
|
||||
free_list_head_ = next;
|
||||
}
|
||||
count_--;
|
||||
return true;
|
||||
}
|
||||
|
||||
intptr_t current = array_[pos].next;
|
||||
|
||||
// If there's only the single element in the bucket and it does not match the
|
||||
// key to be removed, just return.
|
||||
if (current == kNil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the case where the second element in the bucket is the one to be
|
||||
// removed.
|
||||
if (KeyValueTrait::KeyOf(lists_[current].kv) == key) {
|
||||
array_[pos].next = lists_[current].next;
|
||||
lists_[current] = HashMapListElement();
|
||||
lists_[current].next = free_list_head_;
|
||||
free_list_head_ = current;
|
||||
count_--;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finally, iterate through the rest of the bucket to see if we can find the
|
||||
// entry that matches our key.
|
||||
intptr_t previous;
|
||||
while (KeyValueTrait::KeyOf(lists_[current].kv) != key) {
|
||||
previous = current;
|
||||
current = lists_[current].next;
|
||||
|
||||
if (current == kNil) {
|
||||
// Could not find entry with provided key to remove.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
lists_[previous].next = lists_[current].next;
|
||||
lists_[current] = HashMapListElement();
|
||||
lists_[current].next = free_list_head_;
|
||||
free_list_head_ = current;
|
||||
count_--;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename KeyValueTrait>
|
||||
class DirectChainedHashMap
|
||||
: public BaseDirectChainedHashMap<KeyValueTrait, ValueObject> {
|
||||
|
|
|
@ -31,6 +31,9 @@ TEST_CASE(DirectChainedHashMap) {
|
|||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
EXPECT(map.LookupValue(&v2) == &v2);
|
||||
EXPECT(map.LookupValue(&v3) == &v1);
|
||||
EXPECT(map.Remove(&v1));
|
||||
EXPECT(map.Lookup(&v1) == NULL);
|
||||
map.Insert(&v1);
|
||||
DirectChainedHashMap<PointerKeyValueTrait<TestValue> > map2(map);
|
||||
EXPECT(map2.LookupValue(&v1) == &v1);
|
||||
EXPECT(map2.LookupValue(&v2) == &v2);
|
||||
|
@ -38,6 +41,64 @@ TEST_CASE(DirectChainedHashMap) {
|
|||
}
|
||||
|
||||
|
||||
TEST_CASE(DirectChainedHashMapInsertRemove) {
|
||||
DirectChainedHashMap<PointerKeyValueTrait<TestValue> > map;
|
||||
EXPECT(map.IsEmpty());
|
||||
TestValue v1(1);
|
||||
TestValue v2(3); // Note: v1, v2, v3 should have the same hash.
|
||||
TestValue v3(5);
|
||||
|
||||
// Start with adding and removing the same element.
|
||||
map.Insert(&v1);
|
||||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
EXPECT(map.Remove(&v1));
|
||||
EXPECT(map.Lookup(&v1) == NULL);
|
||||
|
||||
// Inserting v2 first should put it at the head of the list.
|
||||
map.Insert(&v2);
|
||||
map.Insert(&v1);
|
||||
EXPECT(map.LookupValue(&v2) == &v2);
|
||||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
|
||||
// Check to see if removing the head of the list causes issues.
|
||||
EXPECT(map.Remove(&v2));
|
||||
EXPECT(map.Lookup(&v2) == NULL);
|
||||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
|
||||
// Reinsert v2, which will place it at the back of the hash map list.
|
||||
map.Insert(&v2);
|
||||
EXPECT(map.LookupValue(&v2) == &v2);
|
||||
|
||||
// Remove from the back of the hash map list.
|
||||
EXPECT(map.Remove(&v2));
|
||||
EXPECT(map.Lookup(&v2) == NULL);
|
||||
EXPECT(map.Remove(&v1));
|
||||
EXPECT(map.Lookup(&v1) == NULL);
|
||||
|
||||
// Check to see that removing an invalid element returns false.
|
||||
EXPECT(!map.Remove(&v1));
|
||||
|
||||
// One last case: remove from the middle of a hash map list.
|
||||
map.Insert(&v1);
|
||||
map.Insert(&v2);
|
||||
map.Insert(&v3);
|
||||
|
||||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
EXPECT(map.LookupValue(&v2) == &v2);
|
||||
EXPECT(map.LookupValue(&v3) == &v3);
|
||||
|
||||
EXPECT(map.Remove(&v2));
|
||||
EXPECT(map.LookupValue(&v1) == &v1);
|
||||
EXPECT(map.Lookup(&v2) == NULL);
|
||||
EXPECT(map.LookupValue(&v3) == &v3);
|
||||
|
||||
EXPECT(map.Remove(&v1));
|
||||
EXPECT(map.Remove(&v3));
|
||||
|
||||
EXPECT(map.IsEmpty());
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE(MallocDirectChainedHashMap) {
|
||||
MallocDirectChainedHashMap<PointerKeyValueTrait<TestValue> > map;
|
||||
EXPECT(map.IsEmpty());
|
||||
|
|
|
@ -2,16 +2,267 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#if defined(DART_USE_TCMALLOC)
|
||||
#include "platform/globals.h"
|
||||
|
||||
#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
|
||||
|
||||
#include "vm/malloc_hooks.h"
|
||||
|
||||
#include "gperftools/malloc_hook.h"
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/hash_map.h"
|
||||
#include "vm/lockers.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
void MallocHooks::Init() {
|
||||
// TODO(bkonyi): Implement
|
||||
// A locker-type class to automatically grab and release the
|
||||
// in_malloc_hook_flag_.
|
||||
class MallocHookScope {
|
||||
public:
|
||||
static void InitMallocHookFlag() {
|
||||
ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey);
|
||||
in_malloc_hook_flag_ = OSThread::CreateThreadLocal();
|
||||
OSThread::SetThreadLocal(in_malloc_hook_flag_, 0);
|
||||
}
|
||||
|
||||
static void DestroyMallocHookFlag() {
|
||||
ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
|
||||
OSThread::DeleteThreadLocal(in_malloc_hook_flag_);
|
||||
in_malloc_hook_flag_ = kUnsetThreadLocalKey;
|
||||
}
|
||||
|
||||
MallocHookScope() {
|
||||
ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
|
||||
OSThread::SetThreadLocal(in_malloc_hook_flag_, 1);
|
||||
}
|
||||
|
||||
~MallocHookScope() {
|
||||
ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
|
||||
OSThread::SetThreadLocal(in_malloc_hook_flag_, 0);
|
||||
}
|
||||
|
||||
static bool IsInHook() {
|
||||
ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey);
|
||||
return OSThread::GetThreadLocal(in_malloc_hook_flag_);
|
||||
}
|
||||
|
||||
private:
|
||||
static ThreadLocalKey in_malloc_hook_flag_;
|
||||
|
||||
DISALLOW_ALLOCATION();
|
||||
DISALLOW_COPY_AND_ASSIGN(MallocHookScope);
|
||||
};
|
||||
|
||||
|
||||
// Custom key/value trait specifically for address/size pairs. Unlike
|
||||
// RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
|
||||
class AddressKeyValueTrait {
|
||||
public:
|
||||
typedef const void* Key;
|
||||
typedef intptr_t Value;
|
||||
|
||||
struct Pair {
|
||||
Key key;
|
||||
Value value;
|
||||
Pair() : key(NULL), value(-1) {}
|
||||
Pair(const Key key, const Value& value) : key(key), value(value) {}
|
||||
Pair(const Pair& other) : key(other.key), value(other.value) {}
|
||||
};
|
||||
|
||||
static Key KeyOf(Pair kv) { return kv.key; }
|
||||
static Value ValueOf(Pair kv) { return kv.value; }
|
||||
static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
|
||||
static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
|
||||
};
|
||||
|
||||
|
||||
// Map class that will be used to store mappings between ptr -> allocation size.
|
||||
class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
|
||||
public:
|
||||
typedef AddressKeyValueTrait::Key Key;
|
||||
typedef AddressKeyValueTrait::Value Value;
|
||||
typedef AddressKeyValueTrait::Pair Pair;
|
||||
|
||||
inline void Insert(const Key& key, const Value& value) {
|
||||
Pair pair(key, value);
|
||||
MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
|
||||
}
|
||||
|
||||
inline bool Lookup(const Key& key, Value* value) {
|
||||
ASSERT(value != NULL);
|
||||
Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
|
||||
if (pair == NULL) {
|
||||
return false;
|
||||
} else {
|
||||
*value = pair->value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class MallocHooksState {
|
||||
public:
|
||||
static void RecordAllocHook(const void* ptr, size_t size);
|
||||
static void RecordFreeHook(const void* ptr);
|
||||
|
||||
static bool initialized() { return initialized_; }
|
||||
static void Init() {
|
||||
address_map_ = new AddressMap();
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
|
||||
|
||||
static intptr_t allocation_count() { return allocation_count_; }
|
||||
|
||||
static intptr_t heap_allocated_memory_in_bytes() {
|
||||
return heap_allocated_memory_in_bytes_;
|
||||
}
|
||||
|
||||
static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
|
||||
ASSERT(size >= 0);
|
||||
heap_allocated_memory_in_bytes_ += size;
|
||||
++allocation_count_;
|
||||
}
|
||||
|
||||
static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
|
||||
ASSERT(size >= 0);
|
||||
ASSERT(heap_allocated_memory_in_bytes_ >= size);
|
||||
heap_allocated_memory_in_bytes_ -= size;
|
||||
--allocation_count_;
|
||||
ASSERT(allocation_count_ >= 0);
|
||||
}
|
||||
|
||||
static AddressMap* address_map() { return address_map_; }
|
||||
|
||||
static void ResetStats() {
|
||||
allocation_count_ = 0;
|
||||
heap_allocated_memory_in_bytes_ = 0;
|
||||
address_map_->Clear();
|
||||
}
|
||||
|
||||
static void TearDown() {
|
||||
initialized_ = false;
|
||||
ResetStats();
|
||||
delete address_map_;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool initialized_;
|
||||
static Mutex* malloc_hook_mutex_;
|
||||
static intptr_t allocation_count_;
|
||||
static intptr_t heap_allocated_memory_in_bytes_;
|
||||
static AddressMap* address_map_;
|
||||
|
||||
private:
|
||||
DISALLOW_ALLOCATION();
|
||||
DISALLOW_COPY_AND_ASSIGN(MallocHooksState);
|
||||
};
|
||||
|
||||
|
||||
// MallocHooks state / locks.
|
||||
ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey;
|
||||
bool MallocHooksState::initialized_ = false;
|
||||
Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
|
||||
|
||||
// Memory allocation state information.
|
||||
intptr_t MallocHooksState::allocation_count_ = 0;
|
||||
intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
|
||||
AddressMap* MallocHooksState::address_map_ = NULL;
|
||||
|
||||
|
||||
void MallocHooks::InitOnce() {
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
ASSERT(!MallocHooksState::initialized());
|
||||
|
||||
MallocHookScope::InitMallocHookFlag();
|
||||
MallocHooksState::Init();
|
||||
|
||||
// Register malloc hooks.
|
||||
bool success = false;
|
||||
success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook);
|
||||
ASSERT(success);
|
||||
success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook);
|
||||
ASSERT(success);
|
||||
}
|
||||
|
||||
|
||||
void MallocHooks::TearDown() {
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
ASSERT(MallocHooksState::initialized());
|
||||
|
||||
// Remove malloc hooks.
|
||||
bool success = false;
|
||||
success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
|
||||
ASSERT(success);
|
||||
success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
|
||||
ASSERT(success);
|
||||
|
||||
MallocHooksState::TearDown();
|
||||
MallocHookScope::DestroyMallocHookFlag();
|
||||
}
|
||||
|
||||
|
||||
void MallocHooks::ResetStats() {
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
ASSERT(MallocHooksState::initialized());
|
||||
|
||||
MallocHooksState::ResetStats();
|
||||
}
|
||||
|
||||
|
||||
intptr_t MallocHooks::allocation_count() {
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
return MallocHooksState::allocation_count();
|
||||
}
|
||||
|
||||
|
||||
intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
return MallocHooksState::heap_allocated_memory_in_bytes();
|
||||
}
|
||||
|
||||
|
||||
void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
|
||||
if (MallocHookScope::IsInHook()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the malloc hook flag before grabbing the mutex to avoid calling hooks
|
||||
// again.
|
||||
MallocHookScope mhs;
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
ASSERT(MallocHooksState::initialized());
|
||||
|
||||
if (ptr != NULL) {
|
||||
MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
|
||||
MallocHooksState::address_map()->Insert(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MallocHooksState::RecordFreeHook(const void* ptr) {
|
||||
if (MallocHookScope::IsInHook()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the malloc hook flag before grabbing the mutex to avoid calling hooks
|
||||
// again.
|
||||
MallocHookScope mhs;
|
||||
MutexLocker ml(MallocHooksState::malloc_hook_mutex());
|
||||
ASSERT(MallocHooksState::initialized());
|
||||
|
||||
if (ptr != NULL) {
|
||||
intptr_t size = 0;
|
||||
if (MallocHooksState::address_map()->Lookup(ptr, &size)) {
|
||||
MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size);
|
||||
MallocHooksState::address_map()->Remove(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_USE_TCMALLOC)
|
||||
#endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
|
||||
|
|
|
@ -10,7 +10,13 @@
|
|||
namespace dart {
|
||||
|
||||
class MallocHooks {
|
||||
static void Init();
|
||||
public:
|
||||
static void InitOnce();
|
||||
static void TearDown();
|
||||
static void ResetStats();
|
||||
|
||||
static intptr_t allocation_count();
|
||||
static intptr_t heap_allocated_memory_in_bytes();
|
||||
|
||||
private:
|
||||
DISALLOW_ALLOCATION();
|
||||
|
|
80
runtime/vm/malloc_hooks_test.cc
Normal file
80
runtime/vm/malloc_hooks_test.cc
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "platform/globals.h"
|
||||
|
||||
#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/globals.h"
|
||||
#include "vm/malloc_hooks.h"
|
||||
#include "vm/symbols.h"
|
||||
#include "vm/unit_test.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// Note: for these tests, there is no need to call MallocHooks::Init() or
|
||||
// MallocHooks::TearDown() as this is all done by the VM test framework.
|
||||
|
||||
static void MallocHookTestBufferInitializer(volatile char* buffer,
|
||||
uintptr_t size) {
|
||||
// Run through the buffer and do something. If we don't do this and the memory
|
||||
// in buffer isn't touched, the tcmalloc hooks won't be called.
|
||||
for (uintptr_t i = 0; i < size; ++i) {
|
||||
buffer[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UNIT_TEST_CASE(BasicMallocHookTest) {
|
||||
MallocHooks::ResetStats();
|
||||
EXPECT_EQ(0L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(0L, MallocHooks::heap_allocated_memory_in_bytes());
|
||||
|
||||
const intptr_t buffer_size = 10;
|
||||
char* buffer = new char[buffer_size];
|
||||
MallocHookTestBufferInitializer(buffer, buffer_size);
|
||||
|
||||
EXPECT_EQ(1L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(static_cast<intptr_t>(sizeof(char) * buffer_size),
|
||||
MallocHooks::heap_allocated_memory_in_bytes());
|
||||
|
||||
delete[] buffer;
|
||||
EXPECT_EQ(0L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(0L, MallocHooks::heap_allocated_memory_in_bytes());
|
||||
}
|
||||
|
||||
|
||||
UNIT_TEST_CASE(FreeUnseenMemoryMallocHookTest) {
|
||||
const intptr_t pre_hook_buffer_size = 3;
|
||||
char* pre_hook_buffer = new char[pre_hook_buffer_size];
|
||||
MallocHookTestBufferInitializer(pre_hook_buffer, pre_hook_buffer_size);
|
||||
|
||||
MallocHooks::ResetStats();
|
||||
EXPECT_EQ(0L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(0L, MallocHooks::heap_allocated_memory_in_bytes());
|
||||
|
||||
const intptr_t buffer_size = 10;
|
||||
volatile char* buffer = new char[buffer_size];
|
||||
MallocHookTestBufferInitializer(buffer, buffer_size);
|
||||
|
||||
EXPECT_EQ(1L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(static_cast<intptr_t>(sizeof(char) * buffer_size),
|
||||
MallocHooks::heap_allocated_memory_in_bytes());
|
||||
|
||||
delete[] pre_hook_buffer;
|
||||
EXPECT_EQ(1L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(static_cast<intptr_t>(sizeof(char) * buffer_size),
|
||||
MallocHooks::heap_allocated_memory_in_bytes());
|
||||
|
||||
|
||||
delete[] buffer;
|
||||
EXPECT_EQ(0L, MallocHooks::allocation_count());
|
||||
EXPECT_EQ(0L, MallocHooks::heap_allocated_memory_in_bytes());
|
||||
}
|
||||
|
||||
}; // namespace dart
|
||||
|
||||
#endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT)
|
|
@ -2,16 +2,38 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#if defined(DART_USE_TCMALLOC)
|
||||
#include "platform/globals.h"
|
||||
|
||||
#if !defined(DART_USE_TCMALLOC) || defined(PRODUCT)
|
||||
|
||||
#include "vm/malloc_hooks.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
void MallocHooks::Init() {
|
||||
// TODO(bkonyi): Implement
|
||||
void MallocHooks::InitOnce() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
void MallocHooks::TearDown() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
void MallocHooks::ResetStats() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
|
||||
intptr_t MallocHooks::allocation_count() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(DART_USE_TCMALLOC)
|
||||
#endif // defined(DART_USE_TCMALLOC) || defined(PRODUCT)
|
||||
|
|
|
@ -61,13 +61,6 @@
|
|||
],
|
||||
},
|
||||
}],
|
||||
# The following condition should be kept in sync with the corresponding
|
||||
# configurations in runtime/bin/bin.gypi.
|
||||
['OS == "linux" and asan == 0 and msan == 0 and tsan == 0', {
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
['OS=="android" and _toolset=="host"', {
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
|
@ -112,13 +105,6 @@
|
|||
],
|
||||
},
|
||||
}],
|
||||
# The following condition should be kept in sync with the corresponding
|
||||
# configurations in runtime/bin/bin.gypi.
|
||||
['OS == "linux" and asan == 0 and msan == 0 and tsan == 0', {
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
['OS=="android" and _toolset=="host"', {
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
|
@ -163,13 +149,6 @@
|
|||
],
|
||||
},
|
||||
}],
|
||||
# The following condition should be kept in sync with the corresponding
|
||||
# configurations in runtime/bin/bin.gypi.
|
||||
['OS == "linux" and asan == 0 and msan == 0 and tsan == 0', {
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
['OS=="android" and _toolset=="host"', {
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
|
@ -215,13 +194,6 @@
|
|||
],
|
||||
},
|
||||
}],
|
||||
# The following condition should be kept in sync with the corresponding
|
||||
# configurations in runtime/bin/bin.gypi.
|
||||
['OS == "linux" and asan == 0 and msan == 0 and tsan == 0', {
|
||||
'defines': [
|
||||
'DART_USE_TCMALLOC'
|
||||
],
|
||||
}],
|
||||
['OS=="android" and _toolset=="host"', {
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
|
|
|
@ -286,8 +286,9 @@
|
|||
'longjump.h',
|
||||
'longjump_test.cc',
|
||||
'malloc_hooks.cc',
|
||||
'malloc_hooks_unsupported.cc',
|
||||
'malloc_hooks.h',
|
||||
'malloc_hooks_test.cc',
|
||||
'malloc_hooks_unsupported.cc',
|
||||
'megamorphic_cache_table.cc',
|
||||
'megamorphic_cache_table.h',
|
||||
'memory_region.cc',
|
||||
|
|
Loading…
Reference in a new issue