deep-heap-profile.h revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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
47class 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