OLD | NEW |
| (Empty) |
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_ | |
OLD | NEW |