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 |