| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // --- |
| // Author: Sainbayar Sukhbaatar |
| // Dai Mikurube |
| // |
| // This file contains a class DeepHeapProfile and its public function |
| // DeepHeapProfile::DumpOrderedProfile(). The function works like |
| // HeapProfileTable::FillOrderedProfile(), but dumps directory to files. |
| // |
| // DeepHeapProfile::DumpOrderedProfile() dumps more detailed information about |
| // heap usage, which includes OS-level information such as memory residency and |
| // type information if the type profiler is available. |
| // |
| // DeepHeapProfile::DumpOrderedProfile() uses data stored in HeapProfileTable. |
| // Any code in DeepHeapProfile runs only when DumpOrderedProfile() is called. |
| // It has overhead in dumping, but no overhead in logging. |
| // |
| // It currently works only on Linux including Android. It does nothing in |
| // non-Linux environments. |
| |
| // Note that uint64 is used to represent addresses instead of uintptr_t, and |
| // int is used to represent buffer sizes instead of size_t. |
| // It's for consistency with other TCMalloc functions. ProcMapsIterator uses |
| // uint64 for addresses, and HeapProfileTable::DumpOrderedProfile uses int |
| // for buffer sizes. |
| |
| #ifndef BASE_DEEP_HEAP_PROFILE_H_ |
| #define BASE_DEEP_HEAP_PROFILE_H_ |
| |
| #include "config.h" |
| |
| #if defined(TYPE_PROFILING) |
| #include <typeinfo> |
| #endif |
| |
| #if defined(__linux__) || defined(_WIN32) || defined(_WIN64) |
| #define USE_DEEP_HEAP_PROFILE 1 |
| #endif |
| |
| #include "addressmap-inl.h" |
| #include "heap-profile-table.h" |
| #include "memory_region_map.h" |
| |
| class DeepHeapProfile { |
| public: |
| enum PageFrameType { |
| DUMP_NO_PAGEFRAME = 0, // Dumps nothing about pageframes |
| DUMP_PFN = 1, // Dumps only pageframe numbers (PFNs) |
| DUMP_PAGECOUNT = 2, // Dumps PFNs and pagecounts |
| }; |
| |
| // Constructs a DeepHeapProfile instance. It works as a wrapper of |
| // HeapProfileTable. |
| // |
| // |heap_profile| is a pointer to HeapProfileTable. DeepHeapProfile reads |
| // data in |heap_profile| and forwards operations to |heap_profile| if |
| // DeepHeapProfile is not available (non-Linux). |
| // |prefix| is a prefix of dumped file names. |
| // |pageframe_type| means what information is dumped for pageframes. |
| DeepHeapProfile(HeapProfileTable* heap_profile, |
| const char* prefix, |
| enum PageFrameType pageframe_type); |
| ~DeepHeapProfile(); |
| |
| // Dumps a deep profile into |fd| with using |raw_buffer| of |buffer_size|. |
| // |
| // In addition, a list of buckets is dumped into a ".buckets" file in |
| // descending order of allocated bytes. |
| void DumpOrderedProfile(const char* reason, |
| char raw_buffer[], |
| int buffer_size, |
| RawFD fd); |
| |
| private: |
| #ifdef USE_DEEP_HEAP_PROFILE |
| typedef HeapProfileTable::Stats Stats; |
| typedef HeapProfileTable::Bucket Bucket; |
| typedef HeapProfileTable::AllocValue AllocValue; |
| typedef HeapProfileTable::AllocationMap AllocationMap; |
| |
| enum MapsRegionType { |
| // Bytes of memory which were not recognized with /proc/<pid>/maps. |
| // This size should be 0. |
| ABSENT, |
| |
| // Bytes of memory which is mapped anonymously. |
| // Regions which contain nothing in the last column of /proc/<pid>/maps. |
| ANONYMOUS, |
| |
| // Bytes of memory which is mapped to a executable/non-executable file. |
| // Regions which contain file paths in the last column of /proc/<pid>/maps. |
| FILE_EXEC, |
| FILE_NONEXEC, |
| |
| // Bytes of memory which is labeled [stack] in /proc/<pid>/maps. |
| STACK, |
| |
| // Bytes of memory which is labeled, but not mapped to any file. |
| // Regions which contain non-path strings in the last column of |
| // /proc/<pid>/maps. |
| OTHER, |
| |
| NUMBER_OF_MAPS_REGION_TYPES |
| }; |
| |
| static const char* kMapsRegionTypeDict[NUMBER_OF_MAPS_REGION_TYPES]; |
| |
| // Manages a buffer to keep a text to be dumped to a file. |
| class TextBuffer { |
| public: |
| TextBuffer(char *raw_buffer, int size, RawFD fd) |
| : buffer_(raw_buffer), |
| size_(size), |
| cursor_(0), |
| fd_(fd) { |
| } |
| |
| int Size(); |
| int FilledBytes(); |
| void Clear(); |
| void Flush(); |
| |
| bool AppendChar(char value); |
| bool AppendString(const char* value, int width); |
| bool AppendInt(int value, int width, bool leading_zero); |
| bool AppendLong(long value, int width); |
| bool AppendUnsignedLong(unsigned long value, int width); |
| bool AppendInt64(int64 value, int width); |
| bool AppendBase64(uint64 value, int width); |
| bool AppendPtr(uint64 value, int width); |
| |
| private: |
| bool ForwardCursor(int appended); |
| |
| char *buffer_; |
| int size_; |
| int cursor_; |
| RawFD fd_; |
| DISALLOW_COPY_AND_ASSIGN(TextBuffer); |
| }; |
| |
| // Defines an interface for getting info about memory residence. |
| class MemoryResidenceInfoGetterInterface { |
| public: |
| virtual ~MemoryResidenceInfoGetterInterface(); |
| |
| // Initializes the instance. |
| virtual void Initialize() = 0; |
| |
| // Returns the number of resident (including swapped) bytes of the given |
| // memory region from |first_address| to |last_address| inclusive. |
| virtual size_t CommittedSize(uint64 first_address, |
| uint64 last_address, |
| TextBuffer* buffer) const = 0; |
| |
| // Creates a new platform specific MemoryResidenceInfoGetterInterface. |
| static MemoryResidenceInfoGetterInterface* Create( |
| PageFrameType pageframe_type); |
| |
| virtual bool IsPageCountAvailable() const = 0; |
| |
| protected: |
| MemoryResidenceInfoGetterInterface(); |
| }; |
| |
| #if defined(_WIN32) || defined(_WIN64) |
| // TODO(peria): Implement this class. |
| class MemoryInfoGetterWindows : public MemoryResidenceInfoGetterInterface { |
| public: |
| MemoryInfoGetterWindows(PageFrameType) {} |
| virtual ~MemoryInfoGetterWindows() {} |
| |
| virtual void Initialize(); |
| |
| virtual size_t CommittedSize(uint64 first_address, |
| uint64 last_address, |
| TextBuffer* buffer) const; |
| |
| virtual bool IsPageCountAvailable() const; |
| }; |
| #endif // defined(_WIN32) || defined(_WIN64) |
| |
| #if defined(__linux__) |
| // Implements MemoryResidenceInfoGetterInterface for Linux. |
| class MemoryInfoGetterLinux : public MemoryResidenceInfoGetterInterface { |
| public: |
| MemoryInfoGetterLinux(PageFrameType pageframe_type) |
| : pageframe_type_(pageframe_type), |
| pagemap_fd_(kIllegalRawFD), |
| kpagecount_fd_(kIllegalRawFD) {} |
| virtual ~MemoryInfoGetterLinux() {} |
| |
| // Opens /proc/<pid>/pagemap and stores its file descriptor. |
| // It keeps open while the process is running. |
| // |
| // Note that file descriptors need to be refreshed after fork. |
| virtual void Initialize(); |
| |
| // Returns the number of resident (including swapped) bytes of the given |
| // memory region from |first_address| to |last_address| inclusive. |
| virtual size_t CommittedSize(uint64 first_address, |
| uint64 last_address, |
| TextBuffer* buffer) const; |
| |
| virtual bool IsPageCountAvailable() const; |
| |
| private: |
| struct State { |
| uint64 pfn; |
| bool is_committed; // Currently, we use only this |
| bool is_present; |
| bool is_swapped; |
| bool is_shared; |
| bool is_mmap; |
| }; |
| |
| uint64 ReadPageCount(uint64 pfn) const; |
| |
| // Seeks to the offset of the open pagemap file. |
| // It returns true if succeeded. |
| bool Seek(uint64 address) const; |
| |
| // Reads a pagemap state from the current offset. |
| // It returns true if succeeded. |
| bool Read(State* state, bool get_pfn) const; |
| |
| PageFrameType pageframe_type_; |
| RawFD pagemap_fd_; |
| RawFD kpagecount_fd_; |
| }; |
| #endif // defined(__linux__) |
| |
| // Contains extended information for HeapProfileTable::Bucket. These objects |
| // are managed in a hash table (DeepBucketTable) whose key is an address of |
| // a Bucket and other additional information. |
| struct DeepBucket { |
| public: |
| void UnparseForStats(TextBuffer* buffer); |
| void UnparseForBucketFile(TextBuffer* buffer); |
| |
| Bucket* bucket; |
| #if defined(TYPE_PROFILING) |
| const std::type_info* type; // A type of the object |
| #endif |
| size_t committed_size; // A resident size of this bucket |
| bool is_mmap; // True if the bucket represents a mmap region |
| int id; // A unique ID of the bucket |
| bool is_logged; // True if the stracktrace is logged to a file |
| DeepBucket* next; // A reference to the next entry in the hash table |
| }; |
| |
| // Manages a hash table for DeepBucket. |
| class DeepBucketTable { |
| public: |
| DeepBucketTable(int size, |
| HeapProfileTable::Allocator alloc, |
| HeapProfileTable::DeAllocator dealloc); |
| ~DeepBucketTable(); |
| |
| // Finds a DeepBucket instance corresponding to the given |bucket|, or |
| // creates a new DeepBucket object if it doesn't exist. |
| DeepBucket* Lookup(Bucket* bucket, |
| #if defined(TYPE_PROFILING) |
| const std::type_info* type, |
| #endif |
| bool is_mmap); |
| |
| // Writes stats of the hash table to |buffer| for DumpOrderedProfile. |
| void UnparseForStats(TextBuffer* buffer); |
| |
| // Writes all buckets for a bucket file with using |buffer|. |
| void WriteForBucketFile(const char* prefix, |
| int dump_count, |
| char raw_buffer[], |
| int buffer_size); |
| |
| // Resets 'committed_size' members in DeepBucket objects. |
| void ResetCommittedSize(); |
| |
| // Resets all 'is_loggeed' flags in DeepBucket objects. |
| void ResetIsLogged(); |
| |
| private: |
| // Adds |add| to a |hash_value| for Lookup. |
| inline static void AddToHashValue(uintptr_t add, uintptr_t* hash_value); |
| inline static void FinishHashValue(uintptr_t* hash_value); |
| |
| DeepBucket** table_; |
| size_t table_size_; |
| HeapProfileTable::Allocator alloc_; |
| HeapProfileTable::DeAllocator dealloc_; |
| int bucket_id_; |
| }; |
| |
| class RegionStats { |
| public: |
| RegionStats(): virtual_bytes_(0), committed_bytes_(0) {} |
| ~RegionStats() {} |
| |
| // Initializes 'virtual_bytes_' and 'committed_bytes_'. |
| void Initialize(); |
| |
| // Updates itself to contain the tallies of 'virtual_bytes' and |
| // 'committed_bytes' in the region from |first_adress| to |last_address| |
| // inclusive. |
| uint64 Record( |
| const MemoryResidenceInfoGetterInterface* memory_residence_info_getter, |
| uint64 first_address, |
| uint64 last_address, |
| TextBuffer* buffer); |
| |
| // Writes stats of the region into |buffer| with |name|. |
| void Unparse(const char* name, TextBuffer* buffer); |
| |
| size_t virtual_bytes() const { return virtual_bytes_; } |
| size_t committed_bytes() const { return committed_bytes_; } |
| void AddToVirtualBytes(size_t additional_virtual_bytes) { |
| virtual_bytes_ += additional_virtual_bytes; |
| } |
| void AddToCommittedBytes(size_t additional_committed_bytes) { |
| committed_bytes_ += additional_committed_bytes; |
| } |
| void AddAnotherRegionStat(const RegionStats& other) { |
| virtual_bytes_ += other.virtual_bytes_; |
| committed_bytes_ += other.committed_bytes_; |
| } |
| |
| private: |
| size_t virtual_bytes_; |
| size_t committed_bytes_; |
| DISALLOW_COPY_AND_ASSIGN(RegionStats); |
| }; |
| |
| class GlobalStats { |
| public: |
| // Snapshots and calculates global stats from /proc/<pid>/maps and pagemap. |
| void SnapshotMaps( |
| const MemoryResidenceInfoGetterInterface* memory_residence_info_getter, |
| DeepHeapProfile* deep_profile, |
| TextBuffer* mmap_dump_buffer); |
| |
| // Snapshots allocations by malloc and mmap. |
| void SnapshotAllocations(DeepHeapProfile* deep_profile); |
| |
| // Writes global stats into |buffer|. |
| void Unparse(TextBuffer* buffer); |
| |
| private: |
| // Records both virtual and committed byte counts of malloc and mmap regions |
| // as callback functions for AllocationMap::Iterate(). |
| static void RecordAlloc(const void* pointer, |
| AllocValue* alloc_value, |
| DeepHeapProfile* deep_profile); |
| |
| DeepBucket* GetInformationOfMemoryRegion( |
| const MemoryRegionMap::RegionIterator& mmap_iter, |
| const MemoryResidenceInfoGetterInterface* memory_residence_info_getter, |
| DeepHeapProfile* deep_profile); |
| |
| // All RegionStats members in this class contain the bytes of virtual |
| // memory and committed memory. |
| // TODO(dmikurube): These regions should be classified more precisely later |
| // for more detailed analysis. |
| RegionStats all_[NUMBER_OF_MAPS_REGION_TYPES]; |
| |
| RegionStats unhooked_[NUMBER_OF_MAPS_REGION_TYPES]; |
| |
| // Total bytes of malloc'ed regions. |
| RegionStats profiled_malloc_; |
| |
| // Total bytes of mmap'ed regions. |
| RegionStats profiled_mmap_; |
| }; |
| |
| // Writes reformatted /proc/<pid>/maps into a file "|prefix|.<pid>.maps" |
| // with using |raw_buffer| of |buffer_size|. |
| static void WriteProcMaps(const char* prefix, |
| char raw_buffer[], |
| int buffer_size); |
| |
| // Appends the command line (/proc/pid/cmdline on Linux) into |buffer|. |
| bool AppendCommandLine(TextBuffer* buffer); |
| |
| MemoryResidenceInfoGetterInterface* memory_residence_info_getter_; |
| |
| // Process ID of the last dump. This can change by fork. |
| pid_t most_recent_pid_; |
| |
| GlobalStats stats_; // Stats about total memory. |
| int dump_count_; // The number of dumps. |
| char* filename_prefix_; // Output file prefix. |
| char run_id_[128]; |
| |
| DeepBucketTable deep_table_; |
| |
| enum PageFrameType pageframe_type_; |
| #endif // USE_DEEP_HEAP_PROFILE |
| |
| HeapProfileTable* heap_profile_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeepHeapProfile); |
| }; |
| |
| #endif // BASE_DEEP_HEAP_PROFILE_H_ |