Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // ---
      6 // Author: Sainbayar Sukhbaatar
      7 //         Dai Mikurube
      8 //
      9 // This file contains a class DeepHeapProfile and its public function
     10 // DeepHeapProfile::DumpOrderedProfile().  The function works like
     11 // HeapProfileTable::FillOrderedProfile(), but dumps directory to files.
     12 //
     13 // DeepHeapProfile::DumpOrderedProfile() dumps more detailed information about
     14 // heap usage, which includes OS-level information such as memory residency and
     15 // type information if the type profiler is available.
     16 //
     17 // DeepHeapProfile::DumpOrderedProfile() uses data stored in HeapProfileTable.
     18 // Any code in DeepHeapProfile runs only when DumpOrderedProfile() is called.
     19 // It has overhead in dumping, but no overhead in logging.
     20 //
     21 // It currently works only on Linux including Android.  It does nothing in
     22 // non-Linux environments.
     23 
     24 // Note that uint64 is used to represent addresses instead of uintptr_t, and
     25 // int is used to represent buffer sizes instead of size_t.
     26 // It's for consistency with other TCMalloc functions.  ProcMapsIterator uses
     27 // uint64 for addresses, and HeapProfileTable::DumpOrderedProfile uses int
     28 // for buffer sizes.
     29 
     30 #ifndef BASE_DEEP_HEAP_PROFILE_H_
     31 #define BASE_DEEP_HEAP_PROFILE_H_
     32 
     33 #include "config.h"
     34 
     35 #if defined(TYPE_PROFILING)
     36 #include <typeinfo>
     37 #endif
     38 
     39 #if defined(__linux__)
     40 #define USE_DEEP_HEAP_PROFILE 1
     41 #endif
     42 
     43 #include "addressmap-inl.h"
     44 #include "heap-profile-table.h"
     45 #include "memory_region_map.h"
     46 
     47 class DeepHeapProfile {
     48  public:
     49   enum PageFrameType {
     50     DUMP_NO_PAGEFRAME = 0,  // Dumps nothing about pageframes
     51     DUMP_PFN = 1,           // Dumps only pageframe numbers (PFNs)
     52     DUMP_PAGECOUNT = 2,     // Dumps PFNs and pagecounts
     53   };
     54 
     55   // Constructs a DeepHeapProfile instance.  It works as a wrapper of
     56   // HeapProfileTable.
     57   //
     58   // |heap_profile| is a pointer to HeapProfileTable.  DeepHeapProfile reads
     59   // data in |heap_profile| and forwards operations to |heap_profile| if
     60   // DeepHeapProfile is not available (non-Linux).
     61   // |prefix| is a prefix of dumped file names.
     62   // |pageframe_type| means what information is dumped for pageframes.
     63   DeepHeapProfile(HeapProfileTable* heap_profile,
     64                   const char* prefix,
     65                   enum PageFrameType pageframe_type);
     66   ~DeepHeapProfile();
     67 
     68   // Dumps a deep profile into |fd| with using |raw_buffer| of |buffer_size|.
     69   //
     70   // In addition, a list of buckets is dumped into a ".buckets" file in
     71   // descending order of allocated bytes.
     72   void DumpOrderedProfile(const char* reason,
     73                           char raw_buffer[],
     74                           int buffer_size,
     75                           RawFD fd);
     76 
     77  private:
     78 #ifdef USE_DEEP_HEAP_PROFILE
     79   typedef HeapProfileTable::Stats Stats;
     80   typedef HeapProfileTable::Bucket Bucket;
     81   typedef HeapProfileTable::AllocValue AllocValue;
     82   typedef HeapProfileTable::AllocationMap AllocationMap;
     83 
     84   enum MapsRegionType {
     85     // Bytes of memory which were not recognized with /proc/<pid>/maps.
     86     // This size should be 0.
     87     ABSENT,
     88 
     89     // Bytes of memory which is mapped anonymously.
     90     // Regions which contain nothing in the last column of /proc/<pid>/maps.
     91     ANONYMOUS,
     92 
     93     // Bytes of memory which is mapped to a executable/non-executable file.
     94     // Regions which contain file paths in the last column of /proc/<pid>/maps.
     95     FILE_EXEC,
     96     FILE_NONEXEC,
     97 
     98     // Bytes of memory which is labeled [stack] in /proc/<pid>/maps.
     99     STACK,
    100 
    101     // Bytes of memory which is labeled, but not mapped to any file.
    102     // Regions which contain non-path strings in the last column of
    103     // /proc/<pid>/maps.
    104     OTHER,
    105 
    106     NUMBER_OF_MAPS_REGION_TYPES
    107   };
    108 
    109   static const char* kMapsRegionTypeDict[NUMBER_OF_MAPS_REGION_TYPES];
    110 
    111   // Manages a buffer to keep a text to be dumped to a file.
    112   class TextBuffer {
    113    public:
    114     TextBuffer(char *raw_buffer, int size, RawFD fd)
    115         : buffer_(raw_buffer),
    116           size_(size),
    117           cursor_(0),
    118           fd_(fd) {
    119     }
    120 
    121     int Size();
    122     int FilledBytes();
    123     void Clear();
    124     void Flush();
    125 
    126     bool AppendChar(char value);
    127     bool AppendString(const char* value, int width);
    128     bool AppendInt(int value, int width, bool leading_zero);
    129     bool AppendLong(long value, int width);
    130     bool AppendUnsignedLong(unsigned long value, int width);
    131     bool AppendInt64(int64 value, int width);
    132     bool AppendBase64(uint64 value, int width);
    133     bool AppendPtr(uint64 value, int width);
    134 
    135    private:
    136     bool ForwardCursor(int appended);
    137 
    138     char *buffer_;
    139     int size_;
    140     int cursor_;
    141     RawFD fd_;
    142     DISALLOW_COPY_AND_ASSIGN(TextBuffer);
    143   };
    144 
    145   // Defines an interface for getting info about memory residence.
    146   class MemoryResidenceInfoGetterInterface {
    147    public:
    148     virtual ~MemoryResidenceInfoGetterInterface();
    149 
    150     // Initializes the instance.
    151     virtual void Initialize() = 0;
    152 
    153     // Returns the number of resident (including swapped) bytes of the given
    154     // memory region from |first_address| to |last_address| inclusive.
    155     virtual size_t CommittedSize(uint64 first_address,
    156                                  uint64 last_address,
    157                                  TextBuffer* buffer) const = 0;
    158 
    159     // Creates a new platform specific MemoryResidenceInfoGetterInterface.
    160     static MemoryResidenceInfoGetterInterface* Create(
    161         PageFrameType pageframe_type);
    162 
    163     virtual bool IsPageCountAvailable() const = 0;
    164 
    165    protected:
    166     MemoryResidenceInfoGetterInterface();
    167   };
    168 
    169 #if defined(__linux__)
    170   // Implements MemoryResidenceInfoGetterInterface for Linux.
    171   class MemoryInfoGetterLinux : public MemoryResidenceInfoGetterInterface {
    172    public:
    173     MemoryInfoGetterLinux(PageFrameType pageframe_type)
    174         : pageframe_type_(pageframe_type),
    175           pagemap_fd_(kIllegalRawFD),
    176           kpagecount_fd_(kIllegalRawFD) {}
    177     virtual ~MemoryInfoGetterLinux() {}
    178 
    179     // Opens /proc/<pid>/pagemap and stores its file descriptor.
    180     // It keeps open while the process is running.
    181     //
    182     // Note that file descriptors need to be refreshed after fork.
    183     virtual void Initialize();
    184 
    185     // Returns the number of resident (including swapped) bytes of the given
    186     // memory region from |first_address| to |last_address| inclusive.
    187     virtual size_t CommittedSize(uint64 first_address,
    188                                  uint64 last_address,
    189                                  TextBuffer* buffer) const;
    190 
    191     virtual bool IsPageCountAvailable() const;
    192 
    193    private:
    194     struct State {
    195       uint64 pfn;
    196       bool is_committed;  // Currently, we use only this
    197       bool is_present;
    198       bool is_swapped;
    199       bool is_shared;
    200       bool is_mmap;
    201     };
    202 
    203     uint64 ReadPageCount(uint64 pfn) const;
    204 
    205     // Seeks to the offset of the open pagemap file.
    206     // It returns true if succeeded.
    207     bool Seek(uint64 address) const;
    208 
    209     // Reads a pagemap state from the current offset.
    210     // It returns true if succeeded.
    211     bool Read(State* state, bool get_pfn) const;
    212 
    213     PageFrameType pageframe_type_;
    214     RawFD pagemap_fd_;
    215     RawFD kpagecount_fd_;
    216   };
    217 #endif  // defined(__linux__)
    218 
    219   // Contains extended information for HeapProfileTable::Bucket.  These objects
    220   // are managed in a hash table (DeepBucketTable) whose key is an address of
    221   // a Bucket and other additional information.
    222   struct DeepBucket {
    223    public:
    224     void UnparseForStats(TextBuffer* buffer);
    225     void UnparseForBucketFile(TextBuffer* buffer);
    226 
    227     Bucket* bucket;
    228 #if defined(TYPE_PROFILING)
    229     const std::type_info* type;  // A type of the object
    230 #endif
    231     size_t committed_size;  // A resident size of this bucket
    232     bool is_mmap;  // True if the bucket represents a mmap region
    233     int id;  // A unique ID of the bucket
    234     bool is_logged;  // True if the stracktrace is logged to a file
    235     DeepBucket* next;  // A reference to the next entry in the hash table
    236   };
    237 
    238   // Manages a hash table for DeepBucket.
    239   class DeepBucketTable {
    240    public:
    241     DeepBucketTable(int size,
    242                     HeapProfileTable::Allocator alloc,
    243                     HeapProfileTable::DeAllocator dealloc);
    244     ~DeepBucketTable();
    245 
    246     // Finds a DeepBucket instance corresponding to the given |bucket|, or
    247     // creates a new DeepBucket object if it doesn't exist.
    248     DeepBucket* Lookup(Bucket* bucket,
    249 #if defined(TYPE_PROFILING)
    250                        const std::type_info* type,
    251 #endif
    252                        bool is_mmap);
    253 
    254     // Writes stats of the hash table to |buffer| for DumpOrderedProfile.
    255     void UnparseForStats(TextBuffer* buffer);
    256 
    257     // Writes all buckets for a bucket file with using |buffer|.
    258     void WriteForBucketFile(const char* prefix,
    259                             int dump_count,
    260                             char raw_buffer[],
    261                             int buffer_size);
    262 
    263     // Resets 'committed_size' members in DeepBucket objects.
    264     void ResetCommittedSize();
    265 
    266     // Resets all 'is_loggeed' flags in DeepBucket objects.
    267     void ResetIsLogged();
    268 
    269    private:
    270     // Adds |add| to a |hash_value| for Lookup.
    271     inline static void AddToHashValue(uintptr_t add, uintptr_t* hash_value);
    272     inline static void FinishHashValue(uintptr_t* hash_value);
    273 
    274     DeepBucket** table_;
    275     size_t table_size_;
    276     HeapProfileTable::Allocator alloc_;
    277     HeapProfileTable::DeAllocator dealloc_;
    278     int bucket_id_;
    279   };
    280 
    281   class RegionStats {
    282    public:
    283     RegionStats(): virtual_bytes_(0), committed_bytes_(0) {}
    284     ~RegionStats() {}
    285 
    286     // Initializes 'virtual_bytes_' and 'committed_bytes_'.
    287     void Initialize();
    288 
    289     // Updates itself to contain the tallies of 'virtual_bytes' and
    290     // 'committed_bytes' in the region from |first_adress| to |last_address|
    291     // inclusive.
    292     uint64 Record(
    293         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    294         uint64 first_address,
    295         uint64 last_address,
    296         TextBuffer* buffer);
    297 
    298     // Writes stats of the region into |buffer| with |name|.
    299     void Unparse(const char* name, TextBuffer* buffer);
    300 
    301     size_t virtual_bytes() const { return virtual_bytes_; }
    302     size_t committed_bytes() const { return committed_bytes_; }
    303     void AddToVirtualBytes(size_t additional_virtual_bytes) {
    304       virtual_bytes_ += additional_virtual_bytes;
    305     }
    306     void AddToCommittedBytes(size_t additional_committed_bytes) {
    307       committed_bytes_ += additional_committed_bytes;
    308     }
    309     void AddAnotherRegionStat(const RegionStats& other) {
    310       virtual_bytes_ += other.virtual_bytes_;
    311       committed_bytes_ += other.committed_bytes_;
    312     }
    313 
    314    private:
    315     size_t virtual_bytes_;
    316     size_t committed_bytes_;
    317     DISALLOW_COPY_AND_ASSIGN(RegionStats);
    318   };
    319 
    320   class GlobalStats {
    321    public:
    322     // Snapshots and calculates global stats from /proc/<pid>/maps and pagemap.
    323     void SnapshotMaps(
    324         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    325         DeepHeapProfile* deep_profile,
    326         TextBuffer* mmap_dump_buffer);
    327 
    328     // Snapshots allocations by malloc and mmap.
    329     void SnapshotAllocations(DeepHeapProfile* deep_profile);
    330 
    331     // Writes global stats into |buffer|.
    332     void Unparse(TextBuffer* buffer);
    333 
    334   private:
    335     // Records both virtual and committed byte counts of malloc and mmap regions
    336     // as callback functions for AllocationMap::Iterate().
    337     static void RecordAlloc(const void* pointer,
    338                             AllocValue* alloc_value,
    339                             DeepHeapProfile* deep_profile);
    340 
    341     DeepBucket* GetInformationOfMemoryRegion(
    342         const MemoryRegionMap::RegionIterator& mmap_iter,
    343         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    344         DeepHeapProfile* deep_profile);
    345 
    346     // All RegionStats members in this class contain the bytes of virtual
    347     // memory and committed memory.
    348     // TODO(dmikurube): These regions should be classified more precisely later
    349     // for more detailed analysis.
    350     RegionStats all_[NUMBER_OF_MAPS_REGION_TYPES];
    351 
    352     RegionStats unhooked_[NUMBER_OF_MAPS_REGION_TYPES];
    353 
    354     // Total bytes of malloc'ed regions.
    355     RegionStats profiled_malloc_;
    356 
    357     // Total bytes of mmap'ed regions.
    358     RegionStats profiled_mmap_;
    359   };
    360 
    361   // Writes reformatted /proc/<pid>/maps into a file "|prefix|.<pid>.maps"
    362   // with using |raw_buffer| of |buffer_size|.
    363   static void WriteProcMaps(const char* prefix,
    364                             char raw_buffer[],
    365                             int buffer_size);
    366 
    367   // Appends the command line (/proc/pid/cmdline on Linux) into |buffer|.
    368   bool AppendCommandLine(TextBuffer* buffer);
    369 
    370   MemoryResidenceInfoGetterInterface* memory_residence_info_getter_;
    371 
    372   // Process ID of the last dump.  This can change by fork.
    373   pid_t most_recent_pid_;
    374 
    375   GlobalStats stats_;      // Stats about total memory.
    376   int dump_count_;         // The number of dumps.
    377   char* filename_prefix_;  // Output file prefix.
    378   char run_id_[128];
    379 
    380   DeepBucketTable deep_table_;
    381 
    382   enum PageFrameType pageframe_type_;
    383 #endif  // USE_DEEP_HEAP_PROFILE
    384 
    385   HeapProfileTable* heap_profile_;
    386 
    387   DISALLOW_COPY_AND_ASSIGN(DeepHeapProfile);
    388 };
    389 
    390 #endif  // BASE_DEEP_HEAP_PROFILE_H_
    391