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