Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* Copyright (c) 2006, Google Inc. | 1 /* Copyright (c) 2006, Google Inc. |
| 2 * All rights reserved. | 2 * All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 38 | 38 |
| 39 #ifdef HAVE_PTHREAD | 39 #ifdef HAVE_PTHREAD |
| 40 #include <pthread.h> | 40 #include <pthread.h> |
| 41 #endif | 41 #endif |
| 42 #include <stddef.h> | 42 #include <stddef.h> |
| 43 #include <set> | 43 #include <set> |
| 44 #include "base/stl_allocator.h" | 44 #include "base/stl_allocator.h" |
| 45 #include "base/spinlock.h" | 45 #include "base/spinlock.h" |
| 46 #include "base/thread_annotations.h" | 46 #include "base/thread_annotations.h" |
| 47 #include "base/low_level_alloc.h" | 47 #include "base/low_level_alloc.h" |
| 48 #include "heap-profile-stats.h" | |
| 48 | 49 |
| 49 // TODO(maxim): add a unittest: | 50 // TODO(maxim): add a unittest: |
| 50 // execute a bunch of mmaps and compare memory map what strace logs | 51 // execute a bunch of mmaps and compare memory map what strace logs |
| 51 // execute a bunch of mmap/munmup and compare memory map with | 52 // execute a bunch of mmap/munmup and compare memory map with |
| 52 // own accounting of what those mmaps generated | 53 // own accounting of what those mmaps generated |
| 53 | 54 |
| 54 // Thread-safe class to collect and query the map of all memory regions | 55 // Thread-safe class to collect and query the map of all memory regions |
| 55 // in a process that have been created with mmap, munmap, mremap, sbrk. | 56 // in a process that have been created with mmap, munmap, mremap, sbrk. |
| 56 // For each memory region, we keep track of (and provide to users) | 57 // For each memory region, we keep track of (and provide to users) |
| 57 // the stack trace that allocated that memory region. | 58 // the stack trace that allocated that memory region. |
| 58 // The recorded stack trace depth is bounded by | 59 // The recorded stack trace depth is bounded by |
| 59 // a user-supplied max_stack_depth parameter of Init(). | 60 // a user-supplied max_stack_depth parameter of Init(). |
| 60 // After initialization with Init() | 61 // After initialization with Init() |
| 61 // (which can happened even before global object constructor execution) | 62 // (which can happened even before global object constructor execution) |
| 62 // we collect the map by installing and monitoring MallocHook-s | 63 // we collect the map by installing and monitoring MallocHook-s |
| 63 // to mmap, munmap, mremap, sbrk. | 64 // to mmap, munmap, mremap, sbrk. |
| 64 // At any time one can query this map via provided interface. | 65 // At any time one can query this map via provided interface. |
| 65 // For more details on the design of MemoryRegionMap | 66 // For more details on the design of MemoryRegionMap |
| 66 // see the comment at the top of our .cc file. | 67 // see the comment at the top of our .cc file. |
| 67 class MemoryRegionMap { | 68 class MemoryRegionMap { |
| 68 private: | 69 private: |
| 69 // Max call stack recording depth supported by Init(). Set it to be | 70 // Max call stack recording depth supported by Init(). Set it to be |
| 70 // high enough for all our clients. Note: we do not define storage | 71 // high enough for all our clients. Note: we do not define storage |
| 71 // for this (doing that requires special handling in windows), so | 72 // for this (doing that requires special handling in windows), so |
| 72 // don't take the address of it! | 73 // don't take the address of it! |
| 73 static const int kMaxStackDepth = 32; | 74 static const int kMaxStackDepth = 32; |
| 74 | 75 |
| 76 static const int kHashTableSize = 179999; | |
|
jar (doing other things)
2013/03/08 04:43:31
nit: please justify this magic number in a comment
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Added a comment on this constant, and a detailed d
| |
| 77 | |
| 75 public: | 78 public: |
| 76 // interface ================================================================ | 79 // interface ================================================================ |
| 77 | 80 |
| 78 // Every client of MemoryRegionMap must call Init() before first use, | 81 // Every client of MemoryRegionMap must call Init() before first use, |
| 79 // and Shutdown() after last use. This allows us to reference count | 82 // and Shutdown() after last use. This allows us to reference count |
| 80 // this (singleton) class properly. MemoryRegionMap assumes it's the | 83 // this (singleton) class properly. MemoryRegionMap assumes it's the |
| 81 // only client of MallocHooks, so a client can only register other | 84 // only client of MallocHooks, so a client can only register other |
| 82 // MallocHooks after calling Init() and must unregister them before | 85 // MallocHooks after calling Init() and must unregister them before |
| 83 // calling Shutdown(). | 86 // calling Shutdown(). |
| 84 | 87 |
| 85 // Initialize this module to record memory allocation stack traces. | 88 // Initialize this module to record memory allocation stack traces. |
| 86 // Stack traces that have more than "max_stack_depth" frames | 89 // Stack traces that have more than "max_stack_depth" frames |
| 87 // are automatically shrunk to "max_stack_depth" when they are recorded. | 90 // are automatically shrunk to "max_stack_depth" when they are recorded. |
| 88 // Init() can be called more than once w/o harm, largest max_stack_depth | 91 // Init() can be called more than once w/o harm, largest max_stack_depth |
| 89 // will be the effective one. | 92 // will be the effective one. |
| 93 // It counts mmap and munmap sizes per stack trace if "use_buckets". | |
|
jar (doing other things)
2013/03/08 04:43:31
net: Contrary to the comment, I doubt that "counti
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
| |
| 90 // It will install mmap, munmap, mremap, sbrk hooks | 94 // It will install mmap, munmap, mremap, sbrk hooks |
| 91 // and initialize arena_ and our hook and locks, hence one can use | 95 // and initialize arena_ and our hook and locks, hence one can use |
| 92 // MemoryRegionMap::Lock()/Unlock() to manage the locks. | 96 // MemoryRegionMap::Lock()/Unlock() to manage the locks. |
| 93 // Uses Lock/Unlock inside. | 97 // Uses Lock/Unlock inside. |
| 94 static void Init(int max_stack_depth); | 98 static void Init(int max_stack_depth, bool use_buckets); |
| 95 | 99 |
| 96 // Try to shutdown this module undoing what Init() did. | 100 // Try to shutdown this module undoing what Init() did. |
| 97 // Returns true iff could do full shutdown (or it was not attempted). | 101 // Returns true iff could do full shutdown (or it was not attempted). |
| 98 // Full shutdown is attempted when the number of Shutdown() calls equals | 102 // Full shutdown is attempted when the number of Shutdown() calls equals |
| 99 // the number of Init() calls. | 103 // the number of Init() calls. |
| 100 static bool Shutdown(); | 104 static bool Shutdown(); |
| 101 | 105 |
| 106 // Return true if MemoryRegionMap is initialized and working. | |
|
jar (doing other things)
2013/03/08 04:43:31
I understand what initialized means, but I'm uncle
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Changed the comment to:
Return true if MemoryRegi
| |
| 107 static bool IsWorking(); | |
| 108 | |
| 102 // Locks to protect our internal data structures. | 109 // Locks to protect our internal data structures. |
| 103 // These also protect use of arena_ if our Init() has been done. | 110 // These also protect use of arena_ if our Init() has been done. |
| 104 // The lock is recursive. | 111 // The lock is recursive. |
| 105 static void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_); | 112 static void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_); |
| 106 static void Unlock() UNLOCK_FUNCTION(lock_); | 113 static void Unlock() UNLOCK_FUNCTION(lock_); |
| 107 | 114 |
| 108 // Returns true when the lock is held by this thread (for use in RAW_CHECK-s). | 115 // Returns true when the lock is held by this thread (for use in RAW_CHECK-s). |
| 109 static bool LockIsHeld(); | 116 static bool LockIsHeld(); |
| 110 | 117 |
| 111 // Locker object that acquires the MemoryRegionMap::Lock | 118 // Locker object that acquires the MemoryRegionMap::Lock |
| 112 // for the duration of its lifetime (a C++ scope). | 119 // for the duration of its lifetime (a C++ scope). |
| 113 class LockHolder { | 120 class LockHolder { |
| 114 public: | 121 public: |
| 115 LockHolder() { Lock(); } | 122 LockHolder() { Lock(); } |
| 116 ~LockHolder() { Unlock(); } | 123 ~LockHolder() { Unlock(); } |
| 117 private: | 124 private: |
| 118 DISALLOW_COPY_AND_ASSIGN(LockHolder); | 125 DISALLOW_COPY_AND_ASSIGN(LockHolder); |
| 119 }; | 126 }; |
| 120 | 127 |
| 128 // Profile stats. | |
| 129 typedef HeapProfileStats Stats; | |
| 130 typedef HeapProfileBucket Bucket; | |
|
jar (doing other things)
2013/03/08 04:43:31
What is the advantage of defining smaller typedef
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Removed them.
| |
| 131 | |
| 121 // A memory region that we know about through malloc_hook-s. | 132 // A memory region that we know about through malloc_hook-s. |
| 122 // This is essentially an interface through which MemoryRegionMap | 133 // This is essentially an interface through which MemoryRegionMap |
| 123 // exports the collected data to its clients. Thread-compatible. | 134 // exports the collected data to its clients. Thread-compatible. |
| 124 struct Region { | 135 struct Region { |
| 125 uintptr_t start_addr; // region start address | 136 uintptr_t start_addr; // region start address |
| 126 uintptr_t end_addr; // region end address | 137 uintptr_t end_addr; // region end address |
| 127 int call_stack_depth; // number of caller stack frames that we saved | 138 int call_stack_depth; // number of caller stack frames that we saved |
| 128 const void* call_stack[kMaxStackDepth]; // caller address stack array | 139 const void* call_stack[kMaxStackDepth]; // caller address stack array |
| 129 // filled to call_stack_depth size | 140 // filled to call_stack_depth size |
| 130 bool is_stack; // does this region contain a thread's stack: | 141 bool is_stack; // does this region contain a thread's stack: |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 // Returns success. Uses Lock/Unlock inside. | 218 // Returns success. Uses Lock/Unlock inside. |
| 208 static bool FindRegion(uintptr_t addr, Region* result); | 219 static bool FindRegion(uintptr_t addr, Region* result); |
| 209 | 220 |
| 210 // Find the region that contains stack_top, mark that region as | 221 // Find the region that contains stack_top, mark that region as |
| 211 // a stack region, and write its data into *result if found, | 222 // a stack region, and write its data into *result if found, |
| 212 // in which case *result gets filled so that it stays fully functional | 223 // in which case *result gets filled so that it stays fully functional |
| 213 // even when the underlying region gets removed from MemoryRegionMap. | 224 // even when the underlying region gets removed from MemoryRegionMap. |
| 214 // Returns success. Uses Lock/Unlock inside. | 225 // Returns success. Uses Lock/Unlock inside. |
| 215 static bool FindAndMarkStackRegion(uintptr_t stack_top, Region* result); | 226 static bool FindAndMarkStackRegion(uintptr_t stack_top, Region* result); |
| 216 | 227 |
| 228 // Iterate over the buckets which store mmap and munmap counts per stack | |
| 229 // trace. It calls "callback" for each bucket, and passes "arg" to it. | |
| 230 template<class Type> | |
| 231 static void IterateBuckets(void (*callback)(const Bucket*, Type), Type arg); | |
| 232 | |
| 233 // Get the bucket for the caller stack trace "key" of depth "depth" | |
| 234 // creating the bucket if needed. | |
|
jar (doing other things)
2013/03/08 04:43:31
I couldn't understand what this comment meant.
I h
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Changed the comment. The details of the bucket ta
| |
| 235 static Bucket* GetBucket(int depth, const void* const key[]); | |
| 236 | |
| 217 private: // our internal types ============================================== | 237 private: // our internal types ============================================== |
| 218 | 238 |
| 219 // Region comparator for sorting with STL | 239 // Region comparator for sorting with STL |
| 220 struct RegionCmp { | 240 struct RegionCmp { |
| 221 bool operator()(const Region& x, const Region& y) const { | 241 bool operator()(const Region& x, const Region& y) const { |
| 222 return x.end_addr < y.end_addr; | 242 return x.end_addr < y.end_addr; |
| 223 } | 243 } |
| 224 }; | 244 }; |
| 225 | 245 |
| 226 // We allocate STL objects in our own arena. | 246 // We allocate STL objects in our own arena. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 // Recursion count for the recursive lock. | 308 // Recursion count for the recursive lock. |
| 289 static int recursion_count_; | 309 static int recursion_count_; |
| 290 // The thread id of the thread that's inside the recursive lock. | 310 // The thread id of the thread that's inside the recursive lock. |
| 291 static pthread_t lock_owner_tid_; | 311 static pthread_t lock_owner_tid_; |
| 292 | 312 |
| 293 // Total size of all mapped pages so far | 313 // Total size of all mapped pages so far |
| 294 static int64 map_size_; | 314 static int64 map_size_; |
| 295 // Total size of all unmapped pages so far | 315 // Total size of all unmapped pages so far |
| 296 static int64 unmap_size_; | 316 static int64 unmap_size_; |
| 297 | 317 |
| 318 // Bucket hash table. | |
| 319 static Bucket** bucket_table_; | |
|
jar (doing other things)
2013/03/08 04:43:31
Can you clarify in the comment what this data stru
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
| |
| 320 static int num_buckets_; | |
| 321 | |
| 322 // Number of unprocessed bucket inserts. | |
|
jar (doing other things)
2013/03/08 04:43:31
It is not clear what "unprocessed buckets" are, pr
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
| |
| 323 static int saved_buckets_count_; | |
| 324 | |
| 325 // Unprocessed inserts (must be big enough to hold all mmaps that can be | |
| 326 // caused by a GetBucket call). | |
| 327 // Bucket has no constructor, so that c-tor execution does not interfere | |
| 328 // with the any-time use of the static memory behind saved_buckets. | |
| 329 static Bucket saved_buckets_[20]; | |
|
jar (doing other things)
2013/03/08 04:43:31
Please justify the magic number.
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
| |
| 330 | |
| 331 static const void* saved_buckets_keys_[20][kMaxStackDepth]; | |
| 332 | |
| 298 // helpers ================================================================== | 333 // helpers ================================================================== |
| 299 | 334 |
| 300 // Helper for FindRegion and FindAndMarkStackRegion: | 335 // Helper for FindRegion and FindAndMarkStackRegion: |
| 301 // returns the region covering 'addr' or NULL; assumes our lock_ is held. | 336 // returns the region covering 'addr' or NULL; assumes our lock_ is held. |
| 302 static const Region* DoFindRegionLocked(uintptr_t addr); | 337 static const Region* DoFindRegionLocked(uintptr_t addr); |
| 303 | 338 |
| 304 // Verifying wrapper around regions_->insert(region) | 339 // Verifying wrapper around regions_->insert(region) |
| 305 // To be called to do InsertRegionLocked's work only! | 340 // To be called to do InsertRegionLocked's work only! |
| 306 inline static void DoInsertRegionLocked(const Region& region); | 341 inline static void DoInsertRegionLocked(const Region& region); |
| 307 // Handle regions saved by InsertRegionLocked into a tmp static array | 342 // Handle regions saved by InsertRegionLocked into a tmp static array |
| 308 // by calling insert_func on them. | 343 // by calling insert_func on them. |
| 309 inline static void HandleSavedRegionsLocked( | 344 inline static void HandleSavedRegionsLocked( |
| 310 void (*insert_func)(const Region& region)); | 345 void (*insert_func)(const Region& region)); |
|
jar (doing other things)
2013/03/08 04:43:31
nit: indent should 4 characters.
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
We should not change the existing code that makes
| |
| 346 | |
| 347 // Handle buckets saved by GetBucket into a tmp static array. | |
| 348 inline static void HandleSavedBucketsLocked(); | |
|
jar (doing other things)
2013/03/08 04:43:31
The word "Handle" is a poor choice for a verb.
Bet
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Sounds reasonable. Finally renamed it to Restore-
| |
| 349 | |
| 311 // Wrapper around DoInsertRegionLocked | 350 // Wrapper around DoInsertRegionLocked |
| 312 // that handles the case of recursive allocator calls. | 351 // that handles the case of recursive allocator calls. |
| 313 inline static void InsertRegionLocked(const Region& region); | 352 inline static void InsertRegionLocked(const Region& region); |
| 314 | 353 |
| 315 // Record addition of a memory region at address "start" of size "size" | 354 // Record addition of a memory region at address "start" of size "size" |
| 316 // (called from our mmap/mremap/sbrk hooks). | 355 // (called from our mmap/mremap/sbrk hooks). |
| 317 static void RecordRegionAddition(const void* start, size_t size); | 356 static void RecordRegionAddition(const void* start, size_t size); |
| 318 // Record deletion of a memory region at address "start" of size "size" | 357 // Record deletion of a memory region at address "start" of size "size" |
| 319 // (called from our munmap/mremap/sbrk hooks). | 358 // (called from our munmap/mremap/sbrk hooks). |
| 320 static void RecordRegionRemoval(const void* start, size_t size); | 359 static void RecordRegionRemoval(const void* start, size_t size); |
| 321 | 360 |
| 361 // Record deletion of a memory region in a bucket. | |
|
jar (doing other things)
2013/03/08 04:43:31
You need to explain what the arguments mean.
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
| |
| 362 static void RecordRegionRemovalInBucket(int depth, | |
| 363 const void* const key[], | |
| 364 size_t size); | |
| 365 | |
| 322 // Hooks for MallocHook | 366 // Hooks for MallocHook |
| 323 static void MmapHook(const void* result, | 367 static void MmapHook(const void* result, |
| 324 const void* start, size_t size, | 368 const void* start, size_t size, |
| 325 int prot, int flags, | 369 int prot, int flags, |
| 326 int fd, off_t offset); | 370 int fd, off_t offset); |
| 327 static void MunmapHook(const void* ptr, size_t size); | 371 static void MunmapHook(const void* ptr, size_t size); |
| 328 static void MremapHook(const void* result, const void* old_addr, | 372 static void MremapHook(const void* result, const void* old_addr, |
| 329 size_t old_size, size_t new_size, int flags, | 373 size_t old_size, size_t new_size, int flags, |
| 330 const void* new_addr); | 374 const void* new_addr); |
| 331 static void SbrkHook(const void* result, ptrdiff_t increment); | 375 static void SbrkHook(const void* result, ptrdiff_t increment); |
| 332 | 376 |
| 333 // Log all memory regions; Useful for debugging only. | 377 // Log all memory regions; Useful for debugging only. |
| 334 // Assumes Lock() is held | 378 // Assumes Lock() is held |
| 335 static void LogAllLocked(); | 379 static void LogAllLocked(); |
| 336 | 380 |
| 337 DISALLOW_COPY_AND_ASSIGN(MemoryRegionMap); | 381 DISALLOW_COPY_AND_ASSIGN(MemoryRegionMap); |
| 338 }; | 382 }; |
| 339 | 383 |
| 384 template <class Type> | |
| 385 void MemoryRegionMap::IterateBuckets( | |
|
jar (doing other things)
2013/03/08 04:43:31
nit: this is a line continuation, and should be in
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
This newline style (template in a single line, and
| |
| 386 void (*callback)(const Bucket*, Type), Type arg) { | |
|
jar (doing other things)
2013/03/08 04:43:31
nit: provide meaningful variable names for all arg
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
This |arg| is just an argument passed to |callback
| |
| 387 for (int b = 0; b < kHashTableSize; b++) { | |
|
jar (doing other things)
2013/03/08 04:43:31
nit: Why do you want the source code in the header
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Written at:
https://codereview.chromium.org/123880
| |
| 388 for (Bucket* x = bucket_table_[b]; x != NULL; x = x->next) { | |
|
jar (doing other things)
2013/03/08 04:43:31
nit: avoid use of single letter varible names such
Dai Mikurube (NOT FULLTIME)
2013/03/12 08:24:10
Done.
fyi in general, http://google-styleguide.go
| |
| 389 callback(x, arg); | |
| 390 } | |
| 391 } | |
| 392 } | |
| 393 | |
| 340 #endif // BASE_MEMORY_REGION_MAP_H_ | 394 #endif // BASE_MEMORY_REGION_MAP_H_ |
| OLD | NEW |