123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
- // Copyright (c) 2006, Google Inc.
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- // ---
- // Author: Sanjay Ghemawat
- // Maxim Lifantsev (refactoring)
- //
- #ifndef BASE_HEAP_PROFILE_TABLE_H_
- #define BASE_HEAP_PROFILE_TABLE_H_
- #include "addressmap-inl.h"
- #include "base/basictypes.h"
- #include "base/logging.h" // for RawFD
- #include "heap-profile-stats.h"
- // Table to maintain a heap profile data inside,
- // i.e. the set of currently active heap memory allocations.
- // thread-unsafe and non-reentrant code:
- // each instance object must be used by one thread
- // at a time w/o self-recursion.
- //
- // TODO(maxim): add a unittest for this class.
- class HeapProfileTable {
- public:
- // Extension to be used for heap pforile files.
- static const char kFileExt[];
- // Longest stack trace we record.
- static const int kMaxStackDepth = 32;
- // data types ----------------------------
- // Profile stats.
- typedef HeapProfileStats Stats;
- // Info we can return about an allocation.
- struct AllocInfo {
- size_t object_size; // size of the allocation
- const void* const* call_stack; // call stack that made the allocation call
- int stack_depth; // depth of call_stack
- bool live;
- bool ignored;
- };
- // Info we return about an allocation context.
- // An allocation context is a unique caller stack trace
- // of an allocation operation.
- struct AllocContextInfo : public Stats {
- int stack_depth; // Depth of stack trace
- const void* const* call_stack; // Stack trace
- };
- // Memory (de)allocator interface we'll use.
- typedef void* (*Allocator)(size_t size);
- typedef void (*DeAllocator)(void* ptr);
- // interface ---------------------------
- HeapProfileTable(Allocator alloc, DeAllocator dealloc, bool profile_mmap);
- ~HeapProfileTable();
- // Collect the stack trace for the function that asked to do the
- // allocation for passing to RecordAlloc() below.
- //
- // The stack trace is stored in 'stack'. The stack depth is returned.
- //
- // 'skip_count' gives the number of stack frames between this call
- // and the memory allocation function.
- static int GetCallerStackTrace(int skip_count, void* stack[kMaxStackDepth]);
- // Record an allocation at 'ptr' of 'bytes' bytes. 'stack_depth'
- // and 'call_stack' identifying the function that requested the
- // allocation. They can be generated using GetCallerStackTrace() above.
- void RecordAlloc(const void* ptr, size_t bytes,
- int stack_depth, const void* const call_stack[]);
- // Record the deallocation of memory at 'ptr'.
- void RecordFree(const void* ptr);
- // Return true iff we have recorded an allocation at 'ptr'.
- // If yes, fill *object_size with the allocation byte size.
- bool FindAlloc(const void* ptr, size_t* object_size) const;
- // Same as FindAlloc, but fills all of *info.
- bool FindAllocDetails(const void* ptr, AllocInfo* info) const;
- // Return true iff "ptr" points into a recorded allocation
- // If yes, fill *object_ptr with the actual allocation address
- // and *object_size with the allocation byte size.
- // max_size specifies largest currently possible allocation size.
- bool FindInsideAlloc(const void* ptr, size_t max_size,
- const void** object_ptr, size_t* object_size) const;
- // If "ptr" points to a recorded allocation and it's not marked as live
- // mark it as live and return true. Else return false.
- // All allocations start as non-live.
- bool MarkAsLive(const void* ptr);
- // If "ptr" points to a recorded allocation, mark it as "ignored".
- // Ignored objects are treated like other objects, except that they
- // are skipped in heap checking reports.
- void MarkAsIgnored(const void* ptr);
- // Return current total (de)allocation statistics. It doesn't contain
- // mmap'ed regions.
- const Stats& total() const { return total_; }
- // Allocation data iteration callback: gets passed object pointer and
- // fully-filled AllocInfo.
- typedef void (*AllocIterator)(const void* ptr, const AllocInfo& info);
- // Iterate over the allocation profile data calling "callback"
- // for every allocation.
- void IterateAllocs(AllocIterator callback) const {
- address_map_->Iterate(MapArgsAllocIterator, callback);
- }
- // Allocation context profile data iteration callback
- typedef void (*AllocContextIterator)(const AllocContextInfo& info);
- // Iterate over the allocation context profile data calling "callback"
- // for every allocation context. Allocation contexts are ordered by the
- // size of allocated space.
- void IterateOrderedAllocContexts(AllocContextIterator callback) const;
- // Fill profile data into buffer 'buf' of size 'size'
- // and return the actual size occupied by the dump in 'buf'.
- // The profile buckets are dumped in the decreasing order
- // of currently allocated bytes.
- // We do not provision for 0-terminating 'buf'.
- int FillOrderedProfile(char buf[], int size) const;
- // Cleanup any old profile files matching prefix + ".*" + kFileExt.
- static void CleanupOldProfiles(const char* prefix);
- // Return a snapshot of the current contents of *this.
- // Caller must call ReleaseSnapshot() on result when no longer needed.
- // The result is only valid while this exists and until
- // the snapshot is discarded by calling ReleaseSnapshot().
- class Snapshot;
- Snapshot* TakeSnapshot();
- // Release a previously taken snapshot. snapshot must not
- // be used after this call.
- void ReleaseSnapshot(Snapshot* snapshot);
- // Return a snapshot of every non-live, non-ignored object in *this.
- // If "base" is non-NULL, skip any objects present in "base".
- // As a side-effect, clears the "live" bit on every live object in *this.
- // Caller must call ReleaseSnapshot() on result when no longer needed.
- Snapshot* NonLiveSnapshot(Snapshot* base);
- private:
- // data types ----------------------------
- // Hash table bucket to hold (de)allocation stats
- // for a given allocation call stack trace.
- typedef HeapProfileBucket Bucket;
- // Info stored in the address map
- struct AllocValue {
- // Access to the stack-trace bucket
- Bucket* bucket() const {
- return reinterpret_cast<Bucket*>(bucket_rep & ~uintptr_t(kMask));
- }
- // This also does set_live(false).
- void set_bucket(Bucket* b) { bucket_rep = reinterpret_cast<uintptr_t>(b); }
- size_t bytes; // Number of bytes in this allocation
- // Access to the allocation liveness flag (for leak checking)
- bool live() const { return bucket_rep & kLive; }
- void set_live(bool l) {
- bucket_rep = (bucket_rep & ~uintptr_t(kLive)) | (l ? kLive : 0);
- }
- // Should this allocation be ignored if it looks like a leak?
- bool ignore() const { return bucket_rep & kIgnore; }
- void set_ignore(bool r) {
- bucket_rep = (bucket_rep & ~uintptr_t(kIgnore)) | (r ? kIgnore : 0);
- }
- private:
- // We store a few bits in the bottom bits of bucket_rep.
- // (Alignment is at least four, so we have at least two bits.)
- static const int kLive = 1;
- static const int kIgnore = 2;
- static const int kMask = kLive | kIgnore;
- uintptr_t bucket_rep;
- };
- // helper for FindInsideAlloc
- static size_t AllocValueSize(const AllocValue& v) { return v.bytes; }
- typedef AddressMap<AllocValue> AllocationMap;
- // Arguments that need to be passed DumpBucketIterator callback below.
- struct BufferArgs {
- BufferArgs(char* buf_arg, int buflen_arg, int bufsize_arg)
- : buf(buf_arg),
- buflen(buflen_arg),
- bufsize(bufsize_arg) {
- }
- char* buf;
- int buflen;
- int bufsize;
- DISALLOW_COPY_AND_ASSIGN(BufferArgs);
- };
- // Arguments that need to be passed DumpNonLiveIterator callback below.
- struct DumpArgs {
- DumpArgs(RawFD fd_arg, Stats* profile_stats_arg)
- : fd(fd_arg),
- profile_stats(profile_stats_arg) {
- }
- RawFD fd; // file to write to
- Stats* profile_stats; // stats to update (may be NULL)
- };
- // helpers ----------------------------
- // Unparse bucket b and print its portion of profile dump into buf.
- // We return the amount of space in buf that we use. We start printing
- // at buf + buflen, and promise not to go beyond buf + bufsize.
- // We do not provision for 0-terminating 'buf'.
- //
- // If profile_stats is non-NULL, we update *profile_stats by
- // counting bucket b.
- //
- // "extra" is appended to the unparsed bucket. Typically it is empty,
- // but may be set to something like " heapprofile" for the total
- // bucket to indicate the type of the profile.
- static int UnparseBucket(const Bucket& b,
- char* buf, int buflen, int bufsize,
- const char* extra,
- Stats* profile_stats);
- // Get the bucket for the caller stack trace 'key' of depth 'depth'
- // creating the bucket if needed.
- Bucket* GetBucket(int depth, const void* const key[]);
- // Helper for IterateAllocs to do callback signature conversion
- // from AllocationMap::Iterate to AllocIterator.
- static void MapArgsAllocIterator(const void* ptr, AllocValue* v,
- AllocIterator callback) {
- AllocInfo info;
- info.object_size = v->bytes;
- info.call_stack = v->bucket()->stack;
- info.stack_depth = v->bucket()->depth;
- info.live = v->live();
- info.ignored = v->ignore();
- callback(ptr, info);
- }
- // Helper to dump a bucket.
- inline static void DumpBucketIterator(const Bucket* bucket,
- BufferArgs* args);
- // Helper for DumpNonLiveProfile to do object-granularity
- // heap profile dumping. It gets passed to AllocationMap::Iterate.
- inline static void DumpNonLiveIterator(const void* ptr, AllocValue* v,
- const DumpArgs& args);
- // Helper for IterateOrderedAllocContexts and FillOrderedProfile.
- // Creates a sorted list of Buckets whose length is num_buckets_.
- // The caller is responsible for deallocating the returned list.
- Bucket** MakeSortedBucketList() const;
- // Helper for TakeSnapshot. Saves object to snapshot.
- static void AddToSnapshot(const void* ptr, AllocValue* v, Snapshot* s);
- // Arguments passed to AddIfNonLive
- struct AddNonLiveArgs {
- Snapshot* dest;
- Snapshot* base;
- };
- // Helper for NonLiveSnapshot. Adds the object to the destination
- // snapshot if it is non-live.
- static void AddIfNonLive(const void* ptr, AllocValue* v,
- AddNonLiveArgs* arg);
- // Write contents of "*allocations" as a heap profile to
- // "file_name". "total" must contain the total of all entries in
- // "*allocations".
- static bool WriteProfile(const char* file_name,
- const Bucket& total,
- AllocationMap* allocations);
- // data ----------------------------
- // Memory (de)allocator that we use.
- Allocator alloc_;
- DeAllocator dealloc_;
- // Overall profile stats; we use only the Stats part,
- // but make it a Bucket to pass to UnparseBucket.
- Bucket total_;
- bool profile_mmap_;
- // Bucket hash table for malloc.
- // We hand-craft one instead of using one of the pre-written
- // ones because we do not want to use malloc when operating on the table.
- // It is only few lines of code, so no big deal.
- Bucket** bucket_table_;
- int num_buckets_;
- // Map of all currently allocated objects and mapped regions we know about.
- AllocationMap* address_map_;
- DISALLOW_COPY_AND_ASSIGN(HeapProfileTable);
- };
- class HeapProfileTable::Snapshot {
- public:
- const Stats& total() const { return total_; }
- // Report anything in this snapshot as a leak.
- // May use new/delete for temporary storage.
- // If should_symbolize is true, will fork (which is not threadsafe)
- // to turn addresses into symbol names. Set to false for maximum safety.
- // Also writes a heap profile to "filename" that contains
- // all of the objects in this snapshot.
- void ReportLeaks(const char* checker_name, const char* filename,
- bool should_symbolize);
- // Report the addresses of all leaked objects.
- // May use new/delete for temporary storage.
- void ReportIndividualObjects();
- bool Empty() const {
- return (total_.allocs == 0) && (total_.alloc_size == 0);
- }
- private:
- friend class HeapProfileTable;
- // Total count/size are stored in a Bucket so we can reuse UnparseBucket
- Bucket total_;
- // We share the Buckets managed by the parent table, but have our
- // own object->bucket map.
- AllocationMap map_;
- Snapshot(Allocator alloc, DeAllocator dealloc) : map_(alloc, dealloc) {
- memset(&total_, 0, sizeof(total_));
- }
- // Callback used to populate a Snapshot object with entries found
- // in another allocation map.
- inline void Add(const void* ptr, const AllocValue& v) {
- map_.Insert(ptr, v);
- total_.allocs++;
- total_.alloc_size += v.bytes;
- }
- // Helpers for sorting and generating leak reports
- struct Entry;
- struct ReportState;
- static void ReportCallback(const void* ptr, AllocValue* v, ReportState*);
- static void ReportObject(const void* ptr, AllocValue* v, char*);
- DISALLOW_COPY_AND_ASSIGN(Snapshot);
- };
- #endif // BASE_HEAP_PROFILE_TABLE_H_
|