| 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 |