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__) || defined(_WIN32) || defined(_WIN64)
     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(_WIN32) || defined(_WIN64)
    170   // TODO(peria): Implement this class.
    171   class MemoryInfoGetterWindows : public MemoryResidenceInfoGetterInterface {
    172    public:
    173     MemoryInfoGetterWindows(PageFrameType) {}
    174     virtual ~MemoryInfoGetterWindows() {}
    175 
    176     virtual void Initialize();
    177 
    178     virtual size_t CommittedSize(uint64 first_address,
    179                                  uint64 last_address,
    180                                  TextBuffer* buffer) const;
    181 
    182     virtual bool IsPageCountAvailable() const;
    183   };
    184 #endif  // defined(_WIN32) || defined(_WIN64)
    185 
    186 #if defined(__linux__)
    187   // Implements MemoryResidenceInfoGetterInterface for Linux.
    188   class MemoryInfoGetterLinux : public MemoryResidenceInfoGetterInterface {
    189    public:
    190     MemoryInfoGetterLinux(PageFrameType pageframe_type)
    191         : pageframe_type_(pageframe_type),
    192           pagemap_fd_(kIllegalRawFD),
    193           kpagecount_fd_(kIllegalRawFD) {}
    194     virtual ~MemoryInfoGetterLinux() {}
    195 
    196     // Opens /proc/<pid>/pagemap and stores its file descriptor.
    197     // It keeps open while the process is running.
    198     //
    199     // Note that file descriptors need to be refreshed after fork.
    200     virtual void Initialize();
    201 
    202     // Returns the number of resident (including swapped) bytes of the given
    203     // memory region from |first_address| to |last_address| inclusive.
    204     virtual size_t CommittedSize(uint64 first_address,
    205                                  uint64 last_address,
    206                                  TextBuffer* buffer) const;
    207 
    208     virtual bool IsPageCountAvailable() const;
    209 
    210    private:
    211     struct State {
    212       uint64 pfn;
    213       bool is_committed;  // Currently, we use only this
    214       bool is_present;
    215       bool is_swapped;
    216       bool is_shared;
    217       bool is_mmap;
    218     };
    219 
    220     uint64 ReadPageCount(uint64 pfn) const;
    221 
    222     // Seeks to the offset of the open pagemap file.
    223     // It returns true if succeeded.
    224     bool Seek(uint64 address) const;
    225 
    226     // Reads a pagemap state from the current offset.
    227     // It returns true if succeeded.
    228     bool Read(State* state, bool get_pfn) const;
    229 
    230     PageFrameType pageframe_type_;
    231     RawFD pagemap_fd_;
    232     RawFD kpagecount_fd_;
    233   };
    234 #endif  // defined(__linux__)
    235 
    236   // Contains extended information for HeapProfileTable::Bucket.  These objects
    237   // are managed in a hash table (DeepBucketTable) whose key is an address of
    238   // a Bucket and other additional information.
    239   struct DeepBucket {
    240    public:
    241     void UnparseForStats(TextBuffer* buffer);
    242     void UnparseForBucketFile(TextBuffer* buffer);
    243 
    244     Bucket* bucket;
    245 #if defined(TYPE_PROFILING)
    246     const std::type_info* type;  // A type of the object
    247 #endif
    248     size_t committed_size;  // A resident size of this bucket
    249     bool is_mmap;  // True if the bucket represents a mmap region
    250     int id;  // A unique ID of the bucket
    251     bool is_logged;  // True if the stracktrace is logged to a file
    252     DeepBucket* next;  // A reference to the next entry in the hash table
    253   };
    254 
    255   // Manages a hash table for DeepBucket.
    256   class DeepBucketTable {
    257    public:
    258     DeepBucketTable(int size,
    259                     HeapProfileTable::Allocator alloc,
    260                     HeapProfileTable::DeAllocator dealloc);
    261     ~DeepBucketTable();
    262 
    263     // Finds a DeepBucket instance corresponding to the given |bucket|, or
    264     // creates a new DeepBucket object if it doesn't exist.
    265     DeepBucket* Lookup(Bucket* bucket,
    266 #if defined(TYPE_PROFILING)
    267                        const std::type_info* type,
    268 #endif
    269                        bool is_mmap);
    270 
    271     // Writes stats of the hash table to |buffer| for DumpOrderedProfile.
    272     void UnparseForStats(TextBuffer* buffer);
    273 
    274     // Writes all buckets for a bucket file with using |buffer|.
    275     void WriteForBucketFile(const char* prefix,
    276                             int dump_count,
    277                             char raw_buffer[],
    278                             int buffer_size);
    279 
    280     // Resets 'committed_size' members in DeepBucket objects.
    281     void ResetCommittedSize();
    282 
    283     // Resets all 'is_loggeed' flags in DeepBucket objects.
    284     void ResetIsLogged();
    285 
    286    private:
    287     // Adds |add| to a |hash_value| for Lookup.
    288     inline static void AddToHashValue(uintptr_t add, uintptr_t* hash_value);
    289     inline static void FinishHashValue(uintptr_t* hash_value);
    290 
    291     DeepBucket** table_;
    292     size_t table_size_;
    293     HeapProfileTable::Allocator alloc_;
    294     HeapProfileTable::DeAllocator dealloc_;
    295     int bucket_id_;
    296   };
    297 
    298   class RegionStats {
    299    public:
    300     RegionStats(): virtual_bytes_(0), committed_bytes_(0) {}
    301     ~RegionStats() {}
    302 
    303     // Initializes 'virtual_bytes_' and 'committed_bytes_'.
    304     void Initialize();
    305 
    306     // Updates itself to contain the tallies of 'virtual_bytes' and
    307     // 'committed_bytes' in the region from |first_adress| to |last_address|
    308     // inclusive.
    309     uint64 Record(
    310         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    311         uint64 first_address,
    312         uint64 last_address,
    313         TextBuffer* buffer);
    314 
    315     // Writes stats of the region into |buffer| with |name|.
    316     void Unparse(const char* name, TextBuffer* buffer);
    317 
    318     size_t virtual_bytes() const { return virtual_bytes_; }
    319     size_t committed_bytes() const { return committed_bytes_; }
    320     void AddToVirtualBytes(size_t additional_virtual_bytes) {
    321       virtual_bytes_ += additional_virtual_bytes;
    322     }
    323     void AddToCommittedBytes(size_t additional_committed_bytes) {
    324       committed_bytes_ += additional_committed_bytes;
    325     }
    326     void AddAnotherRegionStat(const RegionStats& other) {
    327       virtual_bytes_ += other.virtual_bytes_;
    328       committed_bytes_ += other.committed_bytes_;
    329     }
    330 
    331    private:
    332     size_t virtual_bytes_;
    333     size_t committed_bytes_;
    334     DISALLOW_COPY_AND_ASSIGN(RegionStats);
    335   };
    336 
    337   class GlobalStats {
    338    public:
    339     // Snapshots and calculates global stats from /proc/<pid>/maps and pagemap.
    340     void SnapshotMaps(
    341         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    342         DeepHeapProfile* deep_profile,
    343         TextBuffer* mmap_dump_buffer);
    344 
    345     // Snapshots allocations by malloc and mmap.
    346     void SnapshotAllocations(DeepHeapProfile* deep_profile);
    347 
    348     // Writes global stats into |buffer|.
    349     void Unparse(TextBuffer* buffer);
    350 
    351   private:
    352     // Records both virtual and committed byte counts of malloc and mmap regions
    353     // as callback functions for AllocationMap::Iterate().
    354     static void RecordAlloc(const void* pointer,
    355                             AllocValue* alloc_value,
    356                             DeepHeapProfile* deep_profile);
    357 
    358     DeepBucket* GetInformationOfMemoryRegion(
    359         const MemoryRegionMap::RegionIterator& mmap_iter,
    360         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
    361         DeepHeapProfile* deep_profile);
    362 
    363     // All RegionStats members in this class contain the bytes of virtual
    364     // memory and committed memory.
    365     // TODO(dmikurube): These regions should be classified more precisely later
    366     // for more detailed analysis.
    367     RegionStats all_[NUMBER_OF_MAPS_REGION_TYPES];
    368 
    369     RegionStats unhooked_[NUMBER_OF_MAPS_REGION_TYPES];
    370 
    371     // Total bytes of malloc'ed regions.
    372     RegionStats profiled_malloc_;
    373 
    374     // Total bytes of mmap'ed regions.
    375     RegionStats profiled_mmap_;
    376   };
    377 
    378   // Writes reformatted /proc/<pid>/maps into a file "|prefix|.<pid>.maps"
    379   // with using |raw_buffer| of |buffer_size|.
    380   static void WriteProcMaps(const char* prefix,
    381                             char raw_buffer[],
    382                             int buffer_size);
    383 
    384   // Appends the command line (/proc/pid/cmdline on Linux) into |buffer|.
    385   bool AppendCommandLine(TextBuffer* buffer);
    386 
    387   MemoryResidenceInfoGetterInterface* memory_residence_info_getter_;
    388 
    389   // Process ID of the last dump.  This can change by fork.
    390   pid_t most_recent_pid_;
    391 
    392   GlobalStats stats_;      // Stats about total memory.
    393   int dump_count_;         // The number of dumps.
    394   char* filename_prefix_;  // Output file prefix.
    395   char run_id_[128];
    396 
    397   DeepBucketTable deep_table_;
    398 
    399   enum PageFrameType pageframe_type_;
    400 #endif  // USE_DEEP_HEAP_PROFILE
    401 
    402   HeapProfileTable* heap_profile_;
    403 
    404   DISALLOW_COPY_AND_ASSIGN(DeepHeapProfile);
    405 };
    406 
    407 #endif  // BASE_DEEP_HEAP_PROFILE_H_
    408