• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
TextBuffer(char * raw_buffer,int size,RawFD fd)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:
MemoryInfoGetterWindows(PageFrameType)173     MemoryInfoGetterWindows(PageFrameType) {}
~MemoryInfoGetterWindows()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:
MemoryInfoGetterLinux(PageFrameType pageframe_type)190     MemoryInfoGetterLinux(PageFrameType pageframe_type)
191         : pageframe_type_(pageframe_type),
192           pagemap_fd_(kIllegalRawFD),
193           kpagecount_fd_(kIllegalRawFD) {}
~MemoryInfoGetterLinux()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:
RegionStats()300     RegionStats(): virtual_bytes_(0), committed_bytes_(0) {}
~RegionStats()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 
virtual_bytes()318     size_t virtual_bytes() const { return virtual_bytes_; }
committed_bytes()319     size_t committed_bytes() const { return committed_bytes_; }
AddToVirtualBytes(size_t additional_virtual_bytes)320     void AddToVirtualBytes(size_t additional_virtual_bytes) {
321       virtual_bytes_ += additional_virtual_bytes;
322     }
AddToCommittedBytes(size_t additional_committed_bytes)323     void AddToCommittedBytes(size_t additional_committed_bytes) {
324       committed_bytes_ += additional_committed_bytes;
325     }
AddAnotherRegionStat(const RegionStats & other)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