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

// Table to maintain a heap profile data inside,
// i.e. the set of currently active heap memory allocations.
// Not thread-safe 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[];

  // data types ----------------------------

  // Profile stats.
  struct Stats {
    int32 allocs;      // Number of allocation calls
    int32 frees;       // Number of free calls
    int64 alloc_size;  // Total size of all allocated objects so far
    int64 free_size;   // Total size of all freed objects so far
  };

  // 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
  };

  // Memory (de)allocator interface we'll use.
  typedef void* (*Allocator)(size_t);
  typedef void  (*DeAllocator)(void*);

  // interface ---------------------------

  HeapProfileTable(Allocator alloc, DeAllocator dealloc);
  ~HeapProfileTable();

  // Record an allocation at 'ptr' of 'bytes' bytes.
  // skip_count gives the number of stack frames between this call
  // and the memory allocation function that was asked to do the allocation.
  void RecordAlloc(const void* ptr, size_t bytes, int skip_count);

  // 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 current total (de)allocation statistics.
  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;

  // 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;
  
  // Allocation data dump filtering callback:
  // gets passed object pointer and size
  // needs to return true iff the object is to be filtered out of the dump.
  typedef bool (*DumpFilter)(const void* ptr, size_t size);

  // Dump current heap profile for leak checking purposes to file_name
  // while filtering the objects by "filter".
  // Also write the sums of allocated byte and object counts in the dump
  // to *alloc_bytes and *alloc_objects.
  // dump_alloc_addresses controls if object addresses are dumped.
  bool DumpFilteredProfile(const char* file_name,
                           DumpFilter filter,
                           bool dump_alloc_addresses,
                           Stats* profile_stats) const;

  // Cleanup any old profile files matching prefix + ".*" + kFileExt.
  static void CleanupOldProfiles(const char* prefix);

 private:

  // data types ----------------------------

  // Hash table bucket to hold (de)allocation stats
  // for a given allocation call stack trace.
  struct Bucket : public Stats {
    uintptr_t    hash;   // Hash value of the stack trace
    int          depth;  // Depth of stack trace
    const void** stack;  // Stack trace
    Bucket*      next;   // Next entry in hash-table
  };

  // Info stored in the address map
  struct AllocValue {
    Bucket* bucket;  // The stack-trace bucket
    size_t  bytes;   // Number of bytes in this allocation
  };

  typedef AddressMap<AllocValue> AllocationMap;

  // Arguments that need to be passed FilteredDumpIterator callback below.
  struct DumpArgs {
    int fd;  // file to write to
    bool dump_alloc_addresses;  // if we are dumping allocation's addresses
    DumpFilter filter;  // dumping filter
    Stats* profile_stats;  // stats to update

    DumpArgs(int a, bool b, DumpFilter c, Stats* d)
      : fd(a), dump_alloc_addresses(b), filter(c), profile_stats(d) { }
  };

  // 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'.
  // We update *profile_stats by counting bucket b.
  static int UnparseBucket(const Bucket& b,
                           char* buf, int buflen, int bufsize,
                           Stats* profile_stats);

  // Get the bucket for the current stack trace creating one if needed
  // (skip "skip_count" most recent frames).
  Bucket* GetBucket(int skip_count);

  // Helper for IterateAllocs to do callback signature conversion
  // from AllocationMap::Iterate to AllocIterator.
  static void MapArgsAllocIterator(const void* ptr, AllocValue v,
                                   AllocIterator callback);

  // Helper for DumpFilteredProfile to do object-granularity
  // heap profile dumping. It gets passed to AllocationMap::Iterate.
  static void FilteredDumpIterator(const void* ptr, AllocValue v,
                                   const DumpArgs& args);

  // data ----------------------------

  // Size for table_.
  static const int kHashTableSize = 179999;

  // Longest stack trace we record.
  static const int kMaxStackTrace = 32;

  // 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_;

  // Bucket hash table.
  // 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** table_;
  int num_buckets_;

  // Map of all currently allocated objects we know about.
  AllocationMap* allocation_;

  DISALLOW_EVIL_CONSTRUCTORS(HeapProfileTable);
};

#endif  // BASE_HEAP_PROFILE_TABLE_H__


syntax highlighted by Code2HTML, v. 0.9.1