OLD | NEW |
---|---|
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/globals.h" | 5 #include "platform/globals.h" |
6 | 6 |
7 #if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 7 #if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
8 | 8 |
9 #include "vm/malloc_hooks.h" | 9 #include "vm/malloc_hooks.h" |
10 | 10 |
11 #include "gperftools/malloc_hook.h" | 11 #include "gperftools/malloc_hook.h" |
12 | 12 |
13 #include "platform/assert.h" | 13 #include "platform/assert.h" |
14 #include "vm/hash_map.h" | 14 #include "vm/hash_map.h" |
15 #include "vm/json_stream.h" | 15 #include "vm/json_stream.h" |
16 #include "vm/lockers.h" | 16 #include "vm/os_thread.h" |
17 | 17 |
18 namespace dart { | 18 namespace dart { |
19 | 19 |
20 // A locker-type class to automatically grab and release the | 20 // A locker-type class similar to MutexLocker which tracks which thread |
21 // in_malloc_hook_flag_. | 21 // currently holds the lock. We use this instead of MutexLocker and |
22 class MallocHookScope { | 22 // mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only |
23 // enabled for debug mode. | |
24 class MallocLocker : public ValueObject { | |
zra
2017/02/22 22:54:54
This seems generally useful. Should it go in os_th
bkonyi
2017/02/22 23:04:26
In its current state, I don't think that would wor
siva
2017/02/22 23:53:04
Discussed offline with Zach, For this CL we leave
bkonyi
2017/02/23 00:28:46
That makes sense to me.
| |
23 public: | 25 public: |
24 static void InitMallocHookFlag() { | 26 explicit MallocLocker(Mutex* mutex) : mutex_(mutex) { |
25 MutexLocker ml(malloc_hook_scope_mutex_); | 27 mutex_->Lock(); |
26 ASSERT(in_malloc_hook_flag_ == kUnsetThreadLocalKey); | 28 ASSERT(owner_ == OSThread::kInvalidThreadId); |
27 in_malloc_hook_flag_ = OSThread::CreateThreadLocal(); | 29 owner_ = OSThread::GetCurrentThreadId(); |
28 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | |
29 } | 30 } |
30 | 31 |
31 static void DestroyMallocHookFlag() { | 32 virtual ~MallocLocker() { |
32 MutexLocker ml(malloc_hook_scope_mutex_); | 33 ASSERT(IsOwnedByCurrentThread()); |
33 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | 34 owner_ = OSThread::kInvalidThreadId; |
34 OSThread::DeleteThreadLocal(in_malloc_hook_flag_); | 35 mutex_->Unlock(); |
35 in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
36 } | 36 } |
37 | 37 |
38 MallocHookScope() { | 38 static bool IsOwnedByCurrentThread() { |
39 MutexLocker ml(malloc_hook_scope_mutex_); | 39 return (owner_ == OSThread::GetCurrentThreadId()); |
40 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | |
41 OSThread::SetThreadLocal(in_malloc_hook_flag_, 1); | |
42 } | |
43 | |
44 ~MallocHookScope() { | |
45 MutexLocker ml(malloc_hook_scope_mutex_); | |
46 ASSERT(in_malloc_hook_flag_ != kUnsetThreadLocalKey); | |
47 OSThread::SetThreadLocal(in_malloc_hook_flag_, 0); | |
48 } | |
49 | |
50 static bool IsInHook() { | |
51 MutexLocker ml(malloc_hook_scope_mutex_); | |
52 if (in_malloc_hook_flag_ == kUnsetThreadLocalKey) { | |
53 // Bail out if the malloc hook flag is invalid. This means that | |
54 // MallocHookState::TearDown() has been called and MallocHookScope is no | |
55 // longer intitialized. Don't worry if MallocHookState::TearDown() is | |
56 // called before the hooks grab the mutex, since | |
57 // MallocHooksState::Active() is checked after the lock is taken before | |
58 // proceeding to act on the allocation/free. | |
59 return false; | |
60 } | |
61 return OSThread::GetThreadLocal(in_malloc_hook_flag_); | |
62 } | 40 } |
63 | 41 |
64 private: | 42 private: |
65 static Mutex* malloc_hook_scope_mutex_; | 43 Mutex* mutex_; |
66 static ThreadLocalKey in_malloc_hook_flag_; | 44 static ThreadId owner_; |
67 | 45 |
68 DISALLOW_ALLOCATION(); | 46 DISALLOW_COPY_AND_ASSIGN(MallocLocker); |
zra
2017/02/22 22:54:54
Is this redundant if this is inheriting from Value
bkonyi
2017/02/22 23:04:26
Looks like it is. I had based MallocLocker on Mute
bkonyi
2017/02/23 00:28:46
Done.
| |
69 DISALLOW_COPY_AND_ASSIGN(MallocHookScope); | |
70 }; | 47 }; |
71 | 48 |
72 | 49 |
73 // Custom key/value trait specifically for address/size pairs. Unlike | 50 // Custom key/value trait specifically for address/size pairs. Unlike |
74 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. | 51 // RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry. |
75 class AddressKeyValueTrait { | 52 class AddressKeyValueTrait { |
76 public: | 53 public: |
77 typedef const void* Key; | 54 typedef const void* Key; |
78 typedef intptr_t Value; | 55 typedef intptr_t Value; |
79 | 56 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
180 static intptr_t original_pid_; | 157 static intptr_t original_pid_; |
181 static Mutex* malloc_hook_mutex_; | 158 static Mutex* malloc_hook_mutex_; |
182 static intptr_t allocation_count_; | 159 static intptr_t allocation_count_; |
183 static intptr_t heap_allocated_memory_in_bytes_; | 160 static intptr_t heap_allocated_memory_in_bytes_; |
184 static AddressMap* address_map_; | 161 static AddressMap* address_map_; |
185 | 162 |
186 static const intptr_t kInvalidPid = -1; | 163 static const intptr_t kInvalidPid = -1; |
187 }; | 164 }; |
188 | 165 |
189 | 166 |
190 // MallocHookScope state. | 167 // MallocLocker state. |
191 Mutex* MallocHookScope::malloc_hook_scope_mutex_ = new Mutex(); | 168 ThreadId MallocLocker::owner_ = OSThread::kInvalidThreadId; |
siva
2017/02/22 23:53:04
Shouldn't this be clubbed just below the line :
Mu
bkonyi
2017/02/23 00:28:46
Done.
| |
192 ThreadLocalKey MallocHookScope::in_malloc_hook_flag_ = kUnsetThreadLocalKey; | |
193 | 169 |
194 // MallocHooks state / locks. | 170 // MallocHooks state / locks. |
195 bool MallocHooksState::active_ = false; | 171 bool MallocHooksState::active_ = false; |
196 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; | 172 intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid; |
197 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); | 173 Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex(); |
198 | 174 |
199 // Memory allocation state information. | 175 // Memory allocation state information. |
200 intptr_t MallocHooksState::allocation_count_ = 0; | 176 intptr_t MallocHooksState::allocation_count_ = 0; |
201 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; | 177 intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0; |
202 AddressMap* MallocHooksState::address_map_ = NULL; | 178 AddressMap* MallocHooksState::address_map_ = NULL; |
203 | 179 |
204 | 180 |
205 void MallocHooks::InitOnce() { | 181 void MallocHooks::InitOnce() { |
206 if (!FLAG_enable_malloc_hooks) { | 182 if (!FLAG_enable_malloc_hooks) { |
207 return; | 183 return; |
208 } | 184 } |
209 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 185 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
210 ASSERT(!MallocHooksState::Active()); | 186 ASSERT(!MallocHooksState::Active()); |
211 | 187 |
212 MallocHookScope::InitMallocHookFlag(); | |
213 MallocHooksState::Init(); | 188 MallocHooksState::Init(); |
214 | 189 |
215 // Register malloc hooks. | 190 // Register malloc hooks. |
216 bool success = false; | 191 bool success = false; |
217 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); | 192 success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook); |
218 ASSERT(success); | 193 ASSERT(success); |
219 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); | 194 success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook); |
220 ASSERT(success); | 195 ASSERT(success); |
221 } | 196 } |
222 | 197 |
223 | 198 |
224 void MallocHooks::TearDown() { | 199 void MallocHooks::TearDown() { |
225 if (!FLAG_enable_malloc_hooks) { | 200 if (!FLAG_enable_malloc_hooks) { |
226 return; | 201 return; |
227 } | 202 } |
228 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 203 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
229 ASSERT(MallocHooksState::Active()); | 204 ASSERT(MallocHooksState::Active()); |
230 | 205 |
231 // Remove malloc hooks. | 206 // Remove malloc hooks. |
232 bool success = false; | 207 bool success = false; |
233 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); | 208 success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook); |
234 ASSERT(success); | 209 ASSERT(success); |
235 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); | 210 success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook); |
236 ASSERT(success); | 211 ASSERT(success); |
237 | 212 |
238 MallocHooksState::TearDown(); | 213 MallocHooksState::TearDown(); |
239 MallocHookScope::DestroyMallocHookFlag(); | |
240 } | 214 } |
241 | 215 |
242 | 216 |
243 void MallocHooks::ResetStats() { | 217 void MallocHooks::ResetStats() { |
244 if (!FLAG_enable_malloc_hooks) { | 218 if (!FLAG_enable_malloc_hooks) { |
245 return; | 219 return; |
246 } | 220 } |
247 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 221 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
248 if (MallocHooksState::Active()) { | 222 if (MallocHooksState::Active()) { |
249 MallocHooksState::ResetStats(); | 223 MallocHooksState::ResetStats(); |
250 } | 224 } |
251 } | 225 } |
252 | 226 |
253 | 227 |
254 bool MallocHooks::Active() { | 228 bool MallocHooks::Active() { |
255 if (!FLAG_enable_malloc_hooks) { | 229 if (!FLAG_enable_malloc_hooks) { |
256 return false; | 230 return false; |
257 } | 231 } |
258 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); | 232 ASSERT(MallocHooksState::malloc_hook_mutex()->IsOwnedByCurrentThread()); |
259 return MallocHooksState::Active(); | 233 return MallocHooksState::Active(); |
260 } | 234 } |
261 | 235 |
262 | 236 |
263 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { | 237 void MallocHooks::PrintToJSONObject(JSONObject* jsobj) { |
264 if (!FLAG_enable_malloc_hooks) { | 238 if (!FLAG_enable_malloc_hooks) { |
265 return; | 239 return; |
266 } | 240 } |
267 intptr_t allocated_memory = 0; | 241 intptr_t allocated_memory = 0; |
268 intptr_t allocation_count = 0; | 242 intptr_t allocation_count = 0; |
269 bool add_usage = false; | 243 bool add_usage = false; |
270 // AddProperty may call malloc which would result in an attempt | 244 // AddProperty may call malloc which would result in an attempt |
271 // to acquire the lock recursively so we extract the values first | 245 // to acquire the lock recursively so we extract the values first |
272 // and then add the JSON properties. | 246 // and then add the JSON properties. |
273 { | 247 { |
274 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 248 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
275 if (Active()) { | 249 if (Active()) { |
276 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); | 250 allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes(); |
277 allocation_count = MallocHooksState::allocation_count(); | 251 allocation_count = MallocHooksState::allocation_count(); |
278 add_usage = true; | 252 add_usage = true; |
279 } | 253 } |
280 } | 254 } |
281 if (add_usage) { | 255 if (add_usage) { |
282 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); | 256 jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory); |
283 jsobj->AddProperty("_heapAllocationCount", allocation_count); | 257 jsobj->AddProperty("_heapAllocationCount", allocation_count); |
284 } | 258 } |
285 } | 259 } |
286 | 260 |
287 | 261 |
288 intptr_t MallocHooks::allocation_count() { | 262 intptr_t MallocHooks::allocation_count() { |
289 if (!FLAG_enable_malloc_hooks) { | 263 if (!FLAG_enable_malloc_hooks) { |
290 return 0; | 264 return 0; |
291 } | 265 } |
292 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 266 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
293 return MallocHooksState::allocation_count(); | 267 return MallocHooksState::allocation_count(); |
294 } | 268 } |
295 | 269 |
296 | 270 |
297 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { | 271 intptr_t MallocHooks::heap_allocated_memory_in_bytes() { |
298 if (!FLAG_enable_malloc_hooks) { | 272 if (!FLAG_enable_malloc_hooks) { |
299 return 0; | 273 return 0; |
300 } | 274 } |
301 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 275 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
302 return MallocHooksState::heap_allocated_memory_in_bytes(); | 276 return MallocHooksState::heap_allocated_memory_in_bytes(); |
303 } | 277 } |
304 | 278 |
305 | 279 |
306 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { | 280 void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) { |
307 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 281 if (MallocLocker::IsOwnedByCurrentThread() || |
282 !MallocHooksState::IsOriginalProcess()) { | |
308 return; | 283 return; |
309 } | 284 } |
310 | 285 |
311 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 286 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
312 // Now that we hold the lock, check to make sure everything is still active. | 287 // Now that we hold the lock, check to make sure everything is still active. |
313 if ((ptr != NULL) && MallocHooksState::Active()) { | 288 if ((ptr != NULL) && MallocHooksState::Active()) { |
314 // Set the malloc hook flag to avoid calling hooks again if memory is | |
315 // allocated/freed below. | |
316 MallocHookScope mhs; | |
317 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); | 289 MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size); |
318 MallocHooksState::address_map()->Insert(ptr, size); | 290 MallocHooksState::address_map()->Insert(ptr, size); |
319 } | 291 } |
320 } | 292 } |
321 | 293 |
322 | 294 |
323 void MallocHooksState::RecordFreeHook(const void* ptr) { | 295 void MallocHooksState::RecordFreeHook(const void* ptr) { |
324 if (MallocHookScope::IsInHook() || !MallocHooksState::IsOriginalProcess()) { | 296 if (MallocLocker::IsOwnedByCurrentThread() || |
297 !MallocHooksState::IsOriginalProcess()) { | |
325 return; | 298 return; |
326 } | 299 } |
327 | 300 |
328 MutexLocker ml(MallocHooksState::malloc_hook_mutex()); | 301 MallocLocker ml(MallocHooksState::malloc_hook_mutex()); |
329 // Now that we hold the lock, check to make sure everything is still active. | 302 // Now that we hold the lock, check to make sure everything is still active. |
330 if ((ptr != NULL) && MallocHooksState::Active()) { | 303 if ((ptr != NULL) && MallocHooksState::Active()) { |
331 // Set the malloc hook flag to avoid calling hooks again if memory is | |
332 // allocated/freed below. | |
333 MallocHookScope mhs; | |
334 intptr_t size = 0; | 304 intptr_t size = 0; |
335 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { | 305 if (MallocHooksState::address_map()->Lookup(ptr, &size)) { |
336 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); | 306 MallocHooksState::DecrementHeapAllocatedMemoryInBytes(size); |
337 MallocHooksState::address_map()->Remove(ptr); | 307 MallocHooksState::address_map()->Remove(ptr); |
338 } | 308 } |
339 } | 309 } |
340 } | 310 } |
341 | 311 |
342 } // namespace dart | 312 } // namespace dart |
343 | 313 |
344 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) | 314 #endif // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) |
OLD | NEW |