Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(252)

Side by Side Diff: runtime/vm/malloc_hooks.cc

Issue 2829833003: [Fuchsia] Grab the number of malloc'd bytes from jemalloc (Closed)
Patch Set: Fix guards Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "platform/globals.h"
6
7 #if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) && \
8 !defined(TARGET_ARCH_DBC) && !defined(HOST_OS_FUCHSIA)
9
10 #include "vm/malloc_hooks.h"
11
12 #include "gperftools/malloc_hook.h"
13
14 #include "platform/assert.h"
15 #include "vm/hash_map.h"
16 #include "vm/json_stream.h"
17 #include "vm/os_thread.h"
18 #include "vm/profiler.h"
19
20 namespace dart {
21
22 class AddressMap;
23
24 // MallocHooksState contains all of the state related to the configuration of
25 // the malloc hooks, allocation information, and locks.
26 class MallocHooksState : public AllStatic {
27 public:
28 static void RecordAllocHook(const void* ptr, size_t size);
29 static void RecordFreeHook(const void* ptr);
30
31 static bool Active() {
32 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
33 return active_;
34 }
35 static void Init();
36
37 static bool ProfilingEnabled() { return (OSThread::TryCurrent() != NULL); }
38
39 static bool stack_trace_collection_enabled() {
40 return stack_trace_collection_enabled_;
41 }
42
43 static void set_stack_trace_collection_enabled(bool enabled) {
44 stack_trace_collection_enabled_ = enabled;
45 }
46
47 static bool IsOriginalProcess() {
48 ASSERT(original_pid_ != kInvalidPid);
49 return original_pid_ == OS::ProcessId();
50 }
51
52 static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
53 static ThreadId* malloc_hook_mutex_owner() {
54 return &malloc_hook_mutex_owner_;
55 }
56 static bool IsLockHeldByCurrentThread() {
57 return (malloc_hook_mutex_owner_ == OSThread::GetCurrentThreadId());
58 }
59
60 static intptr_t allocation_count() { return allocation_count_; }
61
62 static intptr_t heap_allocated_memory_in_bytes() {
63 return heap_allocated_memory_in_bytes_;
64 }
65
66 static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
67 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
68 ASSERT(size >= 0);
69 heap_allocated_memory_in_bytes_ += size;
70 ++allocation_count_;
71 }
72
73 static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
74 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
75 ASSERT(size >= 0);
76 ASSERT(heap_allocated_memory_in_bytes_ >= size);
77 heap_allocated_memory_in_bytes_ -= size;
78 --allocation_count_;
79 ASSERT(allocation_count_ >= 0);
80 }
81
82 static AddressMap* address_map() { return address_map_; }
83
84 static void ResetStats();
85 static void TearDown();
86
87 private:
88 static Mutex* malloc_hook_mutex_;
89 static ThreadId malloc_hook_mutex_owner_;
90
91 // Variables protected by malloc_hook_mutex_.
92 static bool active_;
93 static bool stack_trace_collection_enabled_;
94 static intptr_t allocation_count_;
95 static intptr_t heap_allocated_memory_in_bytes_;
96 static AddressMap* address_map_;
97 // End protected variables.
98
99 static intptr_t original_pid_;
100 static const intptr_t kInvalidPid = -1;
101 };
102
103 // A locker-type class similar to MutexLocker which tracks which thread
104 // currently holds the lock. We use this instead of MutexLocker and
105 // mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only
106 // enabled for debug mode.
107 class MallocLocker : public ValueObject {
108 public:
109 explicit MallocLocker(Mutex* mutex, ThreadId* owner)
110 : mutex_(mutex), owner_(owner) {
111 ASSERT(owner != NULL);
112 mutex_->Lock();
113 ASSERT(*owner_ == OSThread::kInvalidThreadId);
114 *owner_ = OSThread::GetCurrentThreadId();
115 }
116
117 virtual ~MallocLocker() {
118 ASSERT(*owner_ == OSThread::GetCurrentThreadId());
119 *owner_ = OSThread::kInvalidThreadId;
120 mutex_->Unlock();
121 }
122
123 private:
124 Mutex* mutex_;
125 ThreadId* owner_;
126 };
127
128 // AllocationInfo contains all information related to a given allocation
129 // including:
130 // -Allocation size in bytes
131 // -Stack trace corresponding to the location of allocation, if applicable
132 class AllocationInfo {
133 public:
134 AllocationInfo(uword address, intptr_t allocation_size)
135 : sample_(NULL), address_(address), allocation_size_(allocation_size) {
136 // Stack trace collection is disabled when we are in the process of creating
137 // the first OSThread in order to prevent deadlocks.
138 if (MallocHooksState::ProfilingEnabled() &&
139 MallocHooksState::stack_trace_collection_enabled()) {
140 sample_ = Profiler::SampleNativeAllocation(kSkipCount, address,
141 allocation_size);
142 ASSERT(sample_->native_allocation_address() == address_);
143 }
144 }
145
146 Sample* sample() const { return sample_; }
147 intptr_t allocation_size() const { return allocation_size_; }
148
149 private:
150 // Note: sample_ is not owned by AllocationInfo, but by the SampleBuffer
151 // created by the profiler. As such, this is only here to track if the sample
152 // is still associated with a native allocation, and its fields are never
153 // accessed from this class.
154 Sample* sample_;
155 uword address_;
156 intptr_t allocation_size_;
157 };
158
159
160 // Custom key/value trait specifically for address/size pairs. Unlike
161 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
162 class AddressKeyValueTrait : public AllStatic {
163 public:
164 typedef const void* Key;
165 typedef AllocationInfo* Value;
166
167 struct Pair {
168 Key key;
169 Value value;
170 Pair() : key(NULL), value(NULL) {}
171 Pair(const Key key, const Value& value) : key(key), value(value) {}
172 Pair(const Pair& other) : key(other.key), value(other.value) {}
173 };
174
175 static Key KeyOf(Pair kv) { return kv.key; }
176 static Value ValueOf(Pair kv) { return kv.value; }
177 static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
178 static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
179 };
180
181
182 // Map class that will be used to store mappings between ptr -> allocation size.
183 class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
184 public:
185 typedef AddressKeyValueTrait::Key Key;
186 typedef AddressKeyValueTrait::Value Value;
187 typedef AddressKeyValueTrait::Pair Pair;
188
189 virtual ~AddressMap() { Clear(); }
190
191 void Insert(const Key& key, const Value& value) {
192 Pair pair(key, value);
193 MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
194 }
195
196 bool Lookup(const Key& key, Value* value) {
197 ASSERT(value != NULL);
198 Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
199 if (pair == NULL) {
200 return false;
201 } else {
202 *value = pair->value;
203 return true;
204 }
205 }
206
207 void Clear() {
208 Iterator iter = GetIterator();
209 Pair* result = iter.Next();
210 while (result != NULL) {
211 delete result->value;
212 result->value = NULL;
213 result = iter.Next();
214 }
215 MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
216 }
217 };
218
219
220 // MallocHooks state / locks.
221 bool MallocHooksState::active_ = false;
222 bool MallocHooksState::stack_trace_collection_enabled_ = false;
223 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
224 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
225 ThreadId MallocHooksState::malloc_hook_mutex_owner_ =
226 OSThread::kInvalidThreadId;
227
228 // Memory allocation state information.
229 intptr_t MallocHooksState::allocation_count_ = 0;
230 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
231 AddressMap* MallocHooksState::address_map_ = NULL;
232
233
234 void MallocHooksState::Init() {
235 address_map_ = new AddressMap();
236 active_ = true;
237 #if defined(DEBUG)
238 stack_trace_collection_enabled_ = true;
239 #else
240 stack_trace_collection_enabled_ = false;
241 #endif // defined(DEBUG)
242 original_pid_ = OS::ProcessId();
243 }
244
245
246 void MallocHooksState::ResetStats() {
247 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
248 allocation_count_ = 0;
249 heap_allocated_memory_in_bytes_ = 0;
250 address_map_->Clear();
251 }
252
253
254 void MallocHooksState::TearDown() {
255 ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
256 active_ = false;
257 original_pid_ = kInvalidPid;
258 ResetStats();
259 delete address_map_;
260 address_map_ = NULL;
261 }
262
263
264 void MallocHooks::InitOnce() {
265 if (!FLAG_enable_malloc_hooks || MallocHooks::Active()) {
266 return;
267 }
268 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
269 MallocHooksState::malloc_hook_mutex_owner());
270 ASSERT(!MallocHooksState::Active());
271
272 MallocHooksState::Init();
273
274 // Register malloc hooks.
275 bool success = false;
276 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook);
277 ASSERT(success);
278 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook);
279 ASSERT(success);
280 }
281
282
283 void MallocHooks::TearDown() {
284 if (!FLAG_enable_malloc_hooks || !MallocHooks::Active()) {
285 return;
286 }
287 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
288 MallocHooksState::malloc_hook_mutex_owner());
289 ASSERT(MallocHooksState::Active());
290
291 // Remove malloc hooks.
292 bool success = false;
293 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
294 ASSERT(success);
295 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
296 ASSERT(success);
297
298 MallocHooksState::TearDown();
299 }
300
301
302 bool MallocHooks::ProfilingEnabled() {
303 return MallocHooksState::ProfilingEnabled();
304 }
305
306
307 bool MallocHooks::stack_trace_collection_enabled() {
308 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
309 MallocHooksState::malloc_hook_mutex_owner());
310 return MallocHooksState::stack_trace_collection_enabled();
311 }
312
313
314 void MallocHooks::set_stack_trace_collection_enabled(bool enabled) {
315 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
316 MallocHooksState::malloc_hook_mutex_owner());
317 MallocHooksState::set_stack_trace_collection_enabled(enabled);
318 }
319
320
321 void MallocHooks::ResetStats() {
322 if (!FLAG_enable_malloc_hooks) {
323 return;
324 }
325 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
326 MallocHooksState::malloc_hook_mutex_owner());
327 if (MallocHooksState::Active()) {
328 MallocHooksState::ResetStats();
329 }
330 }
331
332
333 bool MallocHooks::Active() {
334 if (!FLAG_enable_malloc_hooks) {
335 return false;
336 }
337 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
338 MallocHooksState::malloc_hook_mutex_owner());
339
340 return MallocHooksState::Active();
341 }
342
343
344 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
345 if (!FLAG_enable_malloc_hooks) {
346 return;
347 }
348 intptr_t allocated_memory = 0;
349 intptr_t allocation_count = 0;
350 bool add_usage = false;
351 // AddProperty may call malloc which would result in an attempt
352 // to acquire the lock recursively so we extract the values first
353 // and then add the JSON properties.
354 {
355 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
356 MallocHooksState::malloc_hook_mutex_owner());
357 if (MallocHooksState::Active()) {
358 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes();
359 allocation_count = MallocHooksState::allocation_count();
360 add_usage = true;
361 }
362 }
363 if (add_usage) {
364 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory);
365 jsobj->AddProperty("_heapAllocationCount", allocation_count);
366 }
367 }
368
369
370 intptr_t MallocHooks::allocation_count() {
371 if (!FLAG_enable_malloc_hooks) {
372 return 0;
373 }
374 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
375 MallocHooksState::malloc_hook_mutex_owner());
376 return MallocHooksState::allocation_count();
377 }
378
379
380 intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
381 if (!FLAG_enable_malloc_hooks) {
382 return 0;
383 }
384 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
385 MallocHooksState::malloc_hook_mutex_owner());
386 return MallocHooksState::heap_allocated_memory_in_bytes();
387 }
388
389
390 Sample* MallocHooks::GetSample(const void* ptr) {
391 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
392 MallocHooksState::malloc_hook_mutex_owner());
393
394 ASSERT(MallocHooksState::Active());
395
396 if (ptr != NULL) {
397 AllocationInfo* allocation_info = NULL;
398 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
399 ASSERT(allocation_info != NULL);
400 return allocation_info->sample();
401 }
402 }
403 return NULL;
404 }
405
406
407 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
408 if (MallocHooksState::IsLockHeldByCurrentThread() ||
409 !MallocHooksState::IsOriginalProcess()) {
410 return;
411 }
412
413 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
414 MallocHooksState::malloc_hook_mutex_owner());
415 // Now that we hold the lock, check to make sure everything is still active.
416 if ((ptr != NULL) && MallocHooksState::Active()) {
417 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
418 MallocHooksState::address_map()->Insert(
419 ptr, new AllocationInfo(reinterpret_cast<uword>(ptr), size));
420 }
421 }
422
423
424 void MallocHooksState::RecordFreeHook(const void* ptr) {
425 if (MallocHooksState::IsLockHeldByCurrentThread() ||
426 !MallocHooksState::IsOriginalProcess()) {
427 return;
428 }
429
430 MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
431 MallocHooksState::malloc_hook_mutex_owner());
432 // Now that we hold the lock, check to make sure everything is still active.
433 if ((ptr != NULL) && MallocHooksState::Active()) {
434 AllocationInfo* allocation_info = NULL;
435 if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
436 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
437 allocation_info->allocation_size());
438 const bool result = MallocHooksState::address_map()->Remove(ptr);
439 ASSERT(result);
440 delete allocation_info;
441 }
442 }
443 }
444
445 } // namespace dart
446
447 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) &&
448 // !defined(TARGET_ARCH_DBC) && !defined(HOST_OS_FUCHSIA)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698