Home | History | Annotate | Download | only in src
      1 /* Copyright (c) 2006, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  *
     30  * ---
     31  * Author: Maxim Lifantsev
     32  */
     33 
     34 #ifndef BASE_MEMORY_REGION_MAP_H_
     35 #define BASE_MEMORY_REGION_MAP_H_
     36 
     37 #include <config.h>
     38 
     39 #ifdef HAVE_PTHREAD
     40 #include <pthread.h>
     41 #endif
     42 #include <stddef.h>
     43 #include <set>
     44 #include "base/stl_allocator.h"
     45 #include "base/spinlock.h"
     46 #include "base/thread_annotations.h"
     47 #include "base/low_level_alloc.h"
     48 #include "heap-profile-stats.h"
     49 
     50 // TODO(maxim): add a unittest:
     51 //  execute a bunch of mmaps and compare memory map what strace logs
     52 //  execute a bunch of mmap/munmup and compare memory map with
     53 //  own accounting of what those mmaps generated
     54 
     55 // Thread-safe class to collect and query the map of all memory regions
     56 // in a process that have been created with mmap, munmap, mremap, sbrk.
     57 // For each memory region, we keep track of (and provide to users)
     58 // the stack trace that allocated that memory region.
     59 // The recorded stack trace depth is bounded by
     60 // a user-supplied max_stack_depth parameter of Init().
     61 // After initialization with Init()
     62 // (which can happened even before global object constructor execution)
     63 // we collect the map by installing and monitoring MallocHook-s
     64 // to mmap, munmap, mremap, sbrk.
     65 // At any time one can query this map via provided interface.
     66 // For more details on the design of MemoryRegionMap
     67 // see the comment at the top of our .cc file.
     68 class MemoryRegionMap {
     69  private:
     70   // Max call stack recording depth supported by Init().  Set it to be
     71   // high enough for all our clients.  Note: we do not define storage
     72   // for this (doing that requires special handling in windows), so
     73   // don't take the address of it!
     74   static const int kMaxStackDepth = 32;
     75 
     76   // Size of the hash table of buckets.  A structure of the bucket table is
     77   // described in heap-profile-stats.h.
     78   static const int kHashTableSize = 179999;
     79 
     80  public:
     81   // interface ================================================================
     82 
     83   // Every client of MemoryRegionMap must call Init() before first use,
     84   // and Shutdown() after last use.  This allows us to reference count
     85   // this (singleton) class properly.  MemoryRegionMap assumes it's the
     86   // only client of MallocHooks, so a client can only register other
     87   // MallocHooks after calling Init() and must unregister them before
     88   // calling Shutdown().
     89 
     90   // Initialize this module to record memory allocation stack traces.
     91   // Stack traces that have more than "max_stack_depth" frames
     92   // are automatically shrunk to "max_stack_depth" when they are recorded.
     93   // Init() can be called more than once w/o harm, largest max_stack_depth
     94   // will be the effective one.
     95   // When "use_buckets" is true, then counts of mmap and munmap sizes will be
     96   // recorded with each stack trace.  If Init() is called more than once, then
     97   // counting will be effective after any call contained "use_buckets" of true.
     98   // It will install mmap, munmap, mremap, sbrk hooks
     99   // and initialize arena_ and our hook and locks, hence one can use
    100   // MemoryRegionMap::Lock()/Unlock() to manage the locks.
    101   // Uses Lock/Unlock inside.
    102   static void Init(int max_stack_depth, bool use_buckets);
    103 
    104   // Try to shutdown this module undoing what Init() did.
    105   // Returns true iff could do full shutdown (or it was not attempted).
    106   // Full shutdown is attempted when the number of Shutdown() calls equals
    107   // the number of Init() calls.
    108   static bool Shutdown();
    109 
    110   // Return true if MemoryRegionMap is initialized and recording, i.e. when
    111   // then number of Init() calls are more than the number of Shutdown() calls.
    112   static bool IsRecordingLocked();
    113 
    114   // Locks to protect our internal data structures.
    115   // These also protect use of arena_ if our Init() has been done.
    116   // The lock is recursive.
    117   static void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_);
    118   static void Unlock() UNLOCK_FUNCTION(lock_);
    119 
    120   // Returns true when the lock is held by this thread (for use in RAW_CHECK-s).
    121   static bool LockIsHeld();
    122 
    123   // Locker object that acquires the MemoryRegionMap::Lock
    124   // for the duration of its lifetime (a C++ scope).
    125   class LockHolder {
    126    public:
    127     LockHolder() { Lock(); }
    128     ~LockHolder() { Unlock(); }
    129    private:
    130     DISALLOW_COPY_AND_ASSIGN(LockHolder);
    131   };
    132 
    133   // A memory region that we know about through malloc_hook-s.
    134   // This is essentially an interface through which MemoryRegionMap
    135   // exports the collected data to its clients.  Thread-compatible.
    136   struct Region {
    137     uintptr_t start_addr;  // region start address
    138     uintptr_t end_addr;  // region end address
    139     int call_stack_depth;  // number of caller stack frames that we saved
    140     const void* call_stack[kMaxStackDepth];  // caller address stack array
    141                                              // filled to call_stack_depth size
    142     bool is_stack;  // does this region contain a thread's stack:
    143                     // a user of MemoryRegionMap supplies this info
    144 
    145     // Convenience accessor for call_stack[0],
    146     // i.e. (the program counter of) the immediate caller
    147     // of this region's allocation function,
    148     // but it also returns NULL when call_stack_depth is 0,
    149     // i.e whe we weren't able to get the call stack.
    150     // This usually happens in recursive calls, when the stack-unwinder
    151     // calls mmap() which in turn calls the stack-unwinder.
    152     uintptr_t caller() const {
    153       return reinterpret_cast<uintptr_t>(call_stack_depth >= 1
    154                                          ? call_stack[0] : NULL);
    155     }
    156 
    157     // Return true iff this region overlaps region x.
    158     bool Overlaps(const Region& x) const {
    159       return start_addr < x.end_addr  &&  end_addr > x.start_addr;
    160     }
    161 
    162    private:  // helpers for MemoryRegionMap
    163     friend class MemoryRegionMap;
    164 
    165     // The ways we create Region-s:
    166     void Create(const void* start, size_t size) {
    167       start_addr = reinterpret_cast<uintptr_t>(start);
    168       end_addr = start_addr + size;
    169       is_stack = false;  // not a stack till marked such
    170       call_stack_depth = 0;
    171       AssertIsConsistent();
    172     }
    173     void set_call_stack_depth(int depth) {
    174       RAW_DCHECK(call_stack_depth == 0, "");  // only one such set is allowed
    175       call_stack_depth = depth;
    176       AssertIsConsistent();
    177     }
    178 
    179     // The ways we modify Region-s:
    180     void set_is_stack() { is_stack = true; }
    181     void set_start_addr(uintptr_t addr) {
    182       start_addr = addr;
    183       AssertIsConsistent();
    184     }
    185     void set_end_addr(uintptr_t addr) {
    186       end_addr = addr;
    187       AssertIsConsistent();
    188     }
    189 
    190     // Verifies that *this contains consistent data, crashes if not the case.
    191     void AssertIsConsistent() const {
    192       RAW_DCHECK(start_addr < end_addr, "");
    193       RAW_DCHECK(call_stack_depth >= 0  &&
    194                  call_stack_depth <= kMaxStackDepth, "");
    195     }
    196 
    197     // Post-default construction helper to make a Region suitable
    198     // for searching in RegionSet regions_.
    199     void SetRegionSetKey(uintptr_t addr) {
    200       // make sure *this has no usable data:
    201       if (DEBUG_MODE) memset(this, 0xFF, sizeof(*this));
    202       end_addr = addr;
    203     }
    204 
    205     // Note: call_stack[kMaxStackDepth] as a member lets us make Region
    206     // a simple self-contained struct with correctly behaving bit-vise copying.
    207     // This simplifies the code of this module but wastes some memory:
    208     // in most-often use case of this module (leak checking)
    209     // only one call_stack element out of kMaxStackDepth is actually needed.
    210     // Making the storage for call_stack variable-sized,
    211     // substantially complicates memory management for the Region-s:
    212     // as they need to be created and manipulated for some time
    213     // w/o any memory allocations, yet are also given out to the users.
    214   };
    215 
    216   // Find the region that covers addr and write its data into *result if found,
    217   // in which case *result gets filled so that it stays fully functional
    218   // even when the underlying region gets removed from MemoryRegionMap.
    219   // Returns success. Uses Lock/Unlock inside.
    220   static bool FindRegion(uintptr_t addr, Region* result);
    221 
    222   // Find the region that contains stack_top, mark that region as
    223   // a stack region, and write its data into *result if found,
    224   // in which case *result gets filled so that it stays fully functional
    225   // even when the underlying region gets removed from MemoryRegionMap.
    226   // Returns success. Uses Lock/Unlock inside.
    227   static bool FindAndMarkStackRegion(uintptr_t stack_top, Region* result);
    228 
    229   // Iterate over the buckets which store mmap and munmap counts per stack
    230   // trace.  It calls "callback" for each bucket, and passes "arg" to it.
    231   template<class Type>
    232   static void IterateBuckets(void (*callback)(const HeapProfileBucket*, Type),
    233                              Type arg);
    234 
    235   // Get the bucket whose caller stack trace is "key".  The stack trace is
    236   // used to a depth of "depth" at most.  The requested bucket is created if
    237   // needed.
    238   // The bucket table is described in heap-profile-stats.h.
    239   static HeapProfileBucket* GetBucket(int depth, const void* const key[]);
    240 
    241  private:  // our internal types ==============================================
    242 
    243   // Region comparator for sorting with STL
    244   struct RegionCmp {
    245     bool operator()(const Region& x, const Region& y) const {
    246       return x.end_addr < y.end_addr;
    247     }
    248   };
    249 
    250   // We allocate STL objects in our own arena.
    251   struct MyAllocator {
    252     static void *Allocate(size_t n) {
    253       return LowLevelAlloc::AllocWithArena(n, arena_);
    254     }
    255     static void Free(const void *p, size_t /* n */) {
    256       LowLevelAlloc::Free(const_cast<void*>(p));
    257     }
    258   };
    259 
    260   // Set of the memory regions
    261   typedef std::set<Region, RegionCmp,
    262               STL_Allocator<Region, MyAllocator> > RegionSet;
    263 
    264  public:  // more in-depth interface ==========================================
    265 
    266   // STL iterator with values of Region
    267   typedef RegionSet::const_iterator RegionIterator;
    268 
    269   // Return the begin/end iterators to all the regions.
    270   // These need Lock/Unlock protection around their whole usage (loop).
    271   // Even when the same thread causes modifications during such a loop
    272   // (which are permitted due to recursive locking)
    273   // the loop iterator will still be valid as long as its region
    274   // has not been deleted, but EndRegionLocked should be
    275   // re-evaluated whenever the set of regions has changed.
    276   static RegionIterator BeginRegionLocked();
    277   static RegionIterator EndRegionLocked();
    278 
    279   // Return the accumulated sizes of mapped and unmapped regions.
    280   static int64 MapSize() { return map_size_; }
    281   static int64 UnmapSize() { return unmap_size_; }
    282 
    283   // Effectively private type from our .cc =================================
    284   // public to let us declare global objects:
    285   union RegionSetRep;
    286 
    287  private:
    288   // representation ===========================================================
    289 
    290   // Counter of clients of this module that have called Init().
    291   static int client_count_;
    292 
    293   // Maximal number of caller stack frames to save (>= 0).
    294   static int max_stack_depth_;
    295 
    296   // Arena used for our allocations in regions_.
    297   static LowLevelAlloc::Arena* arena_;
    298 
    299   // Set of the mmap/sbrk/mremap-ed memory regions
    300   // To be accessed *only* when Lock() is held.
    301   // Hence we protect the non-recursive lock used inside of arena_
    302   // with our recursive Lock(). This lets a user prevent deadlocks
    303   // when threads are stopped by ListAllProcessThreads at random spots
    304   // simply by acquiring our recursive Lock() before that.
    305   static RegionSet* regions_;
    306 
    307   // Lock to protect regions_ and buckets_ variables and the data behind.
    308   static SpinLock lock_;
    309   // Lock to protect the recursive lock itself.
    310   static SpinLock owner_lock_;
    311 
    312   // Recursion count for the recursive lock.
    313   static int recursion_count_;
    314   // The thread id of the thread that's inside the recursive lock.
    315   static pthread_t lock_owner_tid_;
    316 
    317   // Total size of all mapped pages so far
    318   static int64 map_size_;
    319   // Total size of all unmapped pages so far
    320   static int64 unmap_size_;
    321 
    322   // Bucket hash table which is described in heap-profile-stats.h.
    323   static HeapProfileBucket** bucket_table_ GUARDED_BY(lock_);
    324   static int num_buckets_ GUARDED_BY(lock_);
    325 
    326   // The following members are local to MemoryRegionMap::GetBucket()
    327   // and MemoryRegionMap::HandleSavedBucketsLocked()
    328   // and are file-level to ensure that they are initialized at load time.
    329   //
    330   // These are used as temporary storage to break the infinite cycle of mmap
    331   // calling our hook which (sometimes) causes mmap.  It must be a static
    332   // fixed-size array.  The size 20 is just an expected value for safety.
    333   // The details are described in memory_region_map.cc.
    334 
    335   // Number of unprocessed bucket inserts.
    336   static int saved_buckets_count_ GUARDED_BY(lock_);
    337 
    338   // Unprocessed inserts (must be big enough to hold all mmaps that can be
    339   // caused by a GetBucket call).
    340   // Bucket has no constructor, so that c-tor execution does not interfere
    341   // with the any-time use of the static memory behind saved_buckets.
    342   static HeapProfileBucket saved_buckets_[20] GUARDED_BY(lock_);
    343 
    344   static const void* saved_buckets_keys_[20][kMaxStackDepth] GUARDED_BY(lock_);
    345 
    346   // helpers ==================================================================
    347 
    348   // Helper for FindRegion and FindAndMarkStackRegion:
    349   // returns the region covering 'addr' or NULL; assumes our lock_ is held.
    350   static const Region* DoFindRegionLocked(uintptr_t addr);
    351 
    352   // Verifying wrapper around regions_->insert(region)
    353   // To be called to do InsertRegionLocked's work only!
    354   inline static void DoInsertRegionLocked(const Region& region);
    355   // Handle regions saved by InsertRegionLocked into a tmp static array
    356   // by calling insert_func on them.
    357   inline static void HandleSavedRegionsLocked(
    358                        void (*insert_func)(const Region& region));
    359 
    360   // Restore buckets saved in a tmp static array by GetBucket to the bucket
    361   // table where all buckets eventually should be.
    362   static void RestoreSavedBucketsLocked();
    363 
    364   // Initialize RegionSet regions_.
    365   inline static void InitRegionSetLocked();
    366 
    367   // Wrapper around DoInsertRegionLocked
    368   // that handles the case of recursive allocator calls.
    369   inline static void InsertRegionLocked(const Region& region);
    370 
    371   // Record addition of a memory region at address "start" of size "size"
    372   // (called from our mmap/mremap/sbrk hooks).
    373   static void RecordRegionAddition(const void* start, size_t size);
    374   // Record deletion of a memory region at address "start" of size "size"
    375   // (called from our munmap/mremap/sbrk hooks).
    376   static void RecordRegionRemoval(const void* start, size_t size);
    377 
    378   // Record deletion of a memory region of size "size" in a bucket whose
    379   // caller stack trace is "key".  The stack trace is used to a depth of
    380   // "depth" at most.
    381   static void RecordRegionRemovalInBucket(int depth,
    382                                           const void* const key[],
    383                                           size_t size);
    384 
    385   // Hooks for MallocHook
    386   static void MmapHook(const void* result,
    387                        const void* start, size_t size,
    388                        int prot, int flags,
    389                        int fd, off_t offset);
    390   static void MunmapHook(const void* ptr, size_t size);
    391   static void MremapHook(const void* result, const void* old_addr,
    392                          size_t old_size, size_t new_size, int flags,
    393                          const void* new_addr);
    394   static void SbrkHook(const void* result, ptrdiff_t increment);
    395 
    396   // Log all memory regions; Useful for debugging only.
    397   // Assumes Lock() is held
    398   static void LogAllLocked();
    399 
    400   DISALLOW_COPY_AND_ASSIGN(MemoryRegionMap);
    401 };
    402 
    403 template <class Type>
    404 void MemoryRegionMap::IterateBuckets(
    405     void (*callback)(const HeapProfileBucket*, Type), Type callback_arg) {
    406   for (int index = 0; index < kHashTableSize; index++) {
    407     for (HeapProfileBucket* bucket = bucket_table_[index];
    408          bucket != NULL;
    409          bucket = bucket->next) {
    410       callback(bucket, callback_arg);
    411     }
    412   }
    413 }
    414 
    415 #endif  // BASE_MEMORY_REGION_MAP_H_
    416