1// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// ---
31// Author: Sanjay Ghemawat
32//         Maxim Lifantsev (refactoring)
33//
34
35#ifndef BASE_HEAP_PROFILE_TABLE_H_
36#define BASE_HEAP_PROFILE_TABLE_H_
37
38#include "addressmap-inl.h"
39#include "base/basictypes.h"
40#include "base/logging.h"   // for RawFD
41#include "heap-profile-stats.h"
42
43#if defined(TYPE_PROFILING)
44#include <gperftools/type_profiler_map.h>
45#endif  // defined(TYPE_PROFILING)
46
47// Table to maintain a heap profile data inside,
48// i.e. the set of currently active heap memory allocations.
49// thread-unsafe and non-reentrant code:
50// each instance object must be used by one thread
51// at a time w/o self-recursion.
52//
53// TODO(maxim): add a unittest for this class.
54class HeapProfileTable {
55 public:
56
57  // Extension to be used for heap pforile files.
58  static const char kFileExt[];
59
60  // Longest stack trace we record.
61  static const int kMaxStackDepth = 32;
62
63  // data types ----------------------------
64
65  // Profile stats.
66  typedef HeapProfileStats Stats;
67
68  // Possible marks for MarkCurrentAllocations and MarkUnmarkedAllocations. New
69  // allocations are marked with UNMARKED by default.
70  enum AllocationMark {
71    UNMARKED = 0,
72    MARK_ONE,
73    MARK_TWO,
74    MARK_THREE
75  };
76
77  // Info we can return about an allocation.
78  struct AllocInfo {
79    size_t object_size;  // size of the allocation
80    const void* const* call_stack;  // call stack that made the allocation call
81    int stack_depth;  // depth of call_stack
82    bool live;
83    bool ignored;
84  };
85
86  // Info we return about an allocation context.
87  // An allocation context is a unique caller stack trace
88  // of an allocation operation.
89  struct AllocContextInfo : public Stats {
90    int stack_depth;                // Depth of stack trace
91    const void* const* call_stack;  // Stack trace
92  };
93
94  // Memory (de)allocator interface we'll use.
95  typedef void* (*Allocator)(size_t size);
96  typedef void  (*DeAllocator)(void* ptr);
97
98  // interface ---------------------------
99
100  HeapProfileTable(Allocator alloc, DeAllocator dealloc, bool profile_mmap);
101  ~HeapProfileTable();
102
103  // Collect the stack trace for the function that asked to do the
104  // allocation for passing to RecordAlloc() below.
105  //
106  // The stack trace is stored in 'stack'. The stack depth is returned.
107  //
108  // 'skip_count' gives the number of stack frames between this call
109  // and the memory allocation function.
110  static int GetCallerStackTrace(int skip_count, void* stack[kMaxStackDepth]);
111
112  // Record an allocation at 'ptr' of 'bytes' bytes.  'stack_depth'
113  // and 'call_stack' identifying the function that requested the
114  // allocation. They can be generated using GetCallerStackTrace() above.
115  void RecordAlloc(const void* ptr, size_t bytes,
116                   int stack_depth, const void* const call_stack[]);
117
118  // Record the deallocation of memory at 'ptr'.
119  void RecordFree(const void* ptr);
120
121  // Return true iff we have recorded an allocation at 'ptr'.
122  // If yes, fill *object_size with the allocation byte size.
123  bool FindAlloc(const void* ptr, size_t* object_size) const;
124  // Same as FindAlloc, but fills all of *info.
125  bool FindAllocDetails(const void* ptr, AllocInfo* info) const;
126
127  // Return true iff "ptr" points into a recorded allocation
128  // If yes, fill *object_ptr with the actual allocation address
129  // and *object_size with the allocation byte size.
130  // max_size specifies largest currently possible allocation size.
131  bool FindInsideAlloc(const void* ptr, size_t max_size,
132                       const void** object_ptr, size_t* object_size) const;
133
134  // If "ptr" points to a recorded allocation and it's not marked as live
135  // mark it as live and return true. Else return false.
136  // All allocations start as non-live.
137  bool MarkAsLive(const void* ptr);
138
139  // If "ptr" points to a recorded allocation, mark it as "ignored".
140  // Ignored objects are treated like other objects, except that they
141  // are skipped in heap checking reports.
142  void MarkAsIgnored(const void* ptr);
143
144  // Mark all currently known allocations with the given AllocationMark.
145  void MarkCurrentAllocations(AllocationMark mark);
146
147  // Mark all unmarked (i.e. marked with AllocationMark::UNMARKED) with the
148  // given mark.
149  void MarkUnmarkedAllocations(AllocationMark mark);
150
151  // Return current total (de)allocation statistics.  It doesn't contain
152  // mmap'ed regions.
153  const Stats& total() const { return total_; }
154
155  // Allocation data iteration callback: gets passed object pointer and
156  // fully-filled AllocInfo.
157  typedef void (*AllocIterator)(const void* ptr, const AllocInfo& info);
158
159  // Iterate over the allocation profile data calling "callback"
160  // for every allocation.
161  void IterateAllocs(AllocIterator callback) const {
162    address_map_->Iterate(MapArgsAllocIterator, callback);
163  }
164
165  // Callback for iterating through addresses of all allocated objects. Accepts
166  // pointer to user data and object pointer.
167  typedef void (*AddressIterator)(void* data, const void* ptr);
168
169  // Iterate over the addresses of all allocated objects.
170  void IterateAllocationAddresses(AddressIterator, void* data);
171
172  // Allocation context profile data iteration callback
173  typedef void (*AllocContextIterator)(const AllocContextInfo& info);
174
175  // Iterate over the allocation context profile data calling "callback"
176  // for every allocation context. Allocation contexts are ordered by the
177  // size of allocated space.
178  void IterateOrderedAllocContexts(AllocContextIterator callback) const;
179
180  // Fill profile data into buffer 'buf' of size 'size'
181  // and return the actual size occupied by the dump in 'buf'.
182  // The profile buckets are dumped in the decreasing order
183  // of currently allocated bytes.
184  // We do not provision for 0-terminating 'buf'.
185  int FillOrderedProfile(char buf[], int size) const;
186
187  // Cleanup any old profile files matching prefix + ".*" + kFileExt.
188  static void CleanupOldProfiles(const char* prefix);
189
190  // Return a snapshot of the current contents of *this.
191  // Caller must call ReleaseSnapshot() on result when no longer needed.
192  // The result is only valid while this exists and until
193  // the snapshot is discarded by calling ReleaseSnapshot().
194  class Snapshot;
195  Snapshot* TakeSnapshot();
196
197  // Release a previously taken snapshot.  snapshot must not
198  // be used after this call.
199  void ReleaseSnapshot(Snapshot* snapshot);
200
201  // Return a snapshot of every non-live, non-ignored object in *this.
202  // If "base" is non-NULL, skip any objects present in "base".
203  // As a side-effect, clears the "live" bit on every live object in *this.
204  // Caller must call ReleaseSnapshot() on result when no longer needed.
205  Snapshot* NonLiveSnapshot(Snapshot* base);
206
207  // Dump a list of allocations marked as "live" along with their creation
208  // stack traces and sizes to a file named |file_name|. Together with
209  // MarkCurrentAllocatiosn and MarkUnmarkedAllocations this can be used
210  // to find objects that are created in a certain time span:
211  //   1. Invoke MarkCurrentAllocations(MARK_ONE) to mark the start of the
212  //      timespan.
213  //   2. Perform whatever action you suspect allocates memory that is not
214  //      correctly freed.
215  //   3. Invoke MarkUnmarkedAllocations(MARK_TWO).
216  //   4. Perform whatever action is supposed to free the memory again. New
217  //      allocations are not marked. So all allocations that are marked as
218  //      "live" where created during step 2.
219  //   5. Invoke DumpMarkedObjects(MARK_TWO) to get the list of allocations that
220  //      were created during step 2, but survived step 4.
221  //
222  // Note that this functionality cannot be used if the HeapProfileTable is
223  // used for leak checking (using HeapLeakChecker).
224  void DumpMarkedObjects(AllocationMark mark, const char* file_name);
225
226#if defined(TYPE_PROFILING)
227  void DumpTypeStatistics(const char* file_name) const;
228#endif  // defined(TYPE_PROFILING)
229
230 private:
231  friend class DeepHeapProfile;
232
233  // data types ----------------------------
234
235  // Hash table bucket to hold (de)allocation stats
236  // for a given allocation call stack trace.
237  typedef HeapProfileBucket Bucket;
238
239  // Info stored in the address map
240  struct AllocValue {
241    // Access to the stack-trace bucket
242    Bucket* bucket() const {
243      return reinterpret_cast<Bucket*>(bucket_rep & ~uintptr_t(kMask));
244    }
245    // This also does set_live(false).
246    void set_bucket(Bucket* b) { bucket_rep = reinterpret_cast<uintptr_t>(b); }
247    size_t  bytes;   // Number of bytes in this allocation
248
249    // Access to the allocation liveness flag (for leak checking)
250    bool live() const { return bucket_rep & kLive; }
251    void set_live(bool l) {
252      bucket_rep = (bucket_rep & ~uintptr_t(kLive)) | (l ? kLive : 0);
253    }
254
255    // Should this allocation be ignored if it looks like a leak?
256    bool ignore() const { return bucket_rep & kIgnore; }
257    void set_ignore(bool r) {
258      bucket_rep = (bucket_rep & ~uintptr_t(kIgnore)) | (r ? kIgnore : 0);
259    }
260    AllocationMark mark() const {
261      return static_cast<AllocationMark>(bucket_rep & uintptr_t(kMask));
262    }
263    void set_mark(AllocationMark mark) {
264      bucket_rep = (bucket_rep & ~uintptr_t(kMask)) | uintptr_t(mark);
265    }
266
267   private:
268    // We store a few bits in the bottom bits of bucket_rep.
269    // (Alignment is at least four, so we have at least two bits.)
270    static const int kLive = 1;
271    static const int kIgnore = 2;
272    static const int kMask = kLive | kIgnore;
273
274    uintptr_t bucket_rep;
275  };
276
277  // helper for FindInsideAlloc
278  static size_t AllocValueSize(const AllocValue& v) { return v.bytes; }
279
280  typedef AddressMap<AllocValue> AllocationMap;
281
282  // Arguments that need to be passed DumpBucketIterator callback below.
283  struct BufferArgs {
284    BufferArgs(char* buf_arg, int buflen_arg, int bufsize_arg)
285        : buf(buf_arg),
286          buflen(buflen_arg),
287          bufsize(bufsize_arg) {
288    }
289
290    char* buf;
291    int buflen;
292    int bufsize;
293
294    DISALLOW_COPY_AND_ASSIGN(BufferArgs);
295  };
296
297  // Arguments that need to be passed DumpNonLiveIterator callback below.
298  struct DumpArgs {
299    DumpArgs(RawFD fd_arg, Stats* profile_stats_arg)
300        : fd(fd_arg),
301          profile_stats(profile_stats_arg) {
302    }
303
304    RawFD fd;  // file to write to
305    Stats* profile_stats;  // stats to update (may be NULL)
306  };
307
308  // Arguments that need to be passed DumpMarkedIterator callback below.
309  struct DumpMarkedArgs {
310    DumpMarkedArgs(RawFD fd_arg, AllocationMark mark_arg)
311        : fd(fd_arg),
312          mark(mark_arg) {
313    }
314
315    RawFD fd;  // file to write to.
316    AllocationMark mark;  // The mark of the allocations to process.
317  };
318
319  // Arguments that need to be passed MarkIterator callback below.
320  struct MarkArgs {
321    MarkArgs(AllocationMark mark_arg, bool mark_all_arg)
322        : mark(mark_arg),
323          mark_all(mark_all_arg) {
324    }
325
326    AllocationMark mark;  // The mark to put on allocations.
327    bool mark_all;  // True if all allocations should be marked. Otherwise just
328                    // mark unmarked allocations.
329  };
330
331#if defined(TYPE_PROFILING)
332  struct TypeCount {
333    TypeCount(size_t bytes_arg, unsigned int objects_arg)
334        : bytes(bytes_arg),
335          objects(objects_arg) {
336    }
337
338    size_t bytes;
339    unsigned int objects;
340  };
341#endif  // defined(TYPE_PROFILING)
342
343  struct AllocationAddressIteratorArgs {
344    AllocationAddressIteratorArgs(AddressIterator callback_arg, void* data_arg)
345        : callback(callback_arg),
346          data(data_arg) {
347    }
348
349    AddressIterator callback;
350    void* data;
351  };
352
353  // helpers ----------------------------
354
355  // Unparse bucket b and print its portion of profile dump into buf.
356  // We return the amount of space in buf that we use.  We start printing
357  // at buf + buflen, and promise not to go beyond buf + bufsize.
358  // We do not provision for 0-terminating 'buf'.
359  //
360  // If profile_stats is non-NULL, we update *profile_stats by
361  // counting bucket b.
362  //
363  // "extra" is appended to the unparsed bucket.  Typically it is empty,
364  // but may be set to something like " heapprofile" for the total
365  // bucket to indicate the type of the profile.
366  static int UnparseBucket(const Bucket& b,
367                           char* buf, int buflen, int bufsize,
368                           const char* extra,
369                           Stats* profile_stats);
370
371  // Get the bucket for the caller stack trace 'key' of depth 'depth'
372  // creating the bucket if needed.
373  Bucket* GetBucket(int depth, const void* const key[]);
374
375  // Helper for IterateAllocs to do callback signature conversion
376  // from AllocationMap::Iterate to AllocIterator.
377  static void MapArgsAllocIterator(const void* ptr, AllocValue* v,
378                                   AllocIterator callback) {
379    AllocInfo info;
380    info.object_size = v->bytes;
381    info.call_stack = v->bucket()->stack;
382    info.stack_depth = v->bucket()->depth;
383    info.live = v->live();
384    info.ignored = v->ignore();
385    callback(ptr, info);
386  }
387
388  // Helper to dump a bucket.
389  inline static void DumpBucketIterator(const Bucket* bucket,
390                                        BufferArgs* args);
391
392  // Helper for IterateAllocationAddresses.
393  inline static void AllocationAddressesIterator(
394      const void* ptr,
395      AllocValue* v,
396      const AllocationAddressIteratorArgs& args);
397
398  // Helper for MarkCurrentAllocations and MarkUnmarkedAllocations.
399  inline static void MarkIterator(const void* ptr, AllocValue* v,
400                                  const MarkArgs& args);
401
402  // Helper for DumpNonLiveProfile to do object-granularity
403  // heap profile dumping. It gets passed to AllocationMap::Iterate.
404  inline static void DumpNonLiveIterator(const void* ptr, AllocValue* v,
405                                         const DumpArgs& args);
406
407  // Helper for DumpMarkedObjects to dump all allocations with a given mark. It
408  // gets passed to AllocationMap::Iterate.
409  inline static void DumpMarkedIterator(const void* ptr, AllocValue* v,
410                                        const DumpMarkedArgs& args);
411
412#if defined(TYPE_PROFILING)
413  inline static void TallyTypesItererator(const void* ptr,
414                                          AllocValue* value,
415                                          AddressMap<TypeCount>* type_size_map);
416
417  inline static void DumpTypesIterator(const void* ptr,
418                                       TypeCount* size,
419                                       const DumpArgs& args);
420#endif  // defined(TYPE_PROFILING)
421
422  // Helper for IterateOrderedAllocContexts and FillOrderedProfile.
423  // Creates a sorted list of Buckets whose length is num_buckets_.
424  // The caller is responsible for deallocating the returned list.
425  Bucket** MakeSortedBucketList() const;
426
427  // Helper for TakeSnapshot.  Saves object to snapshot.
428  static void AddToSnapshot(const void* ptr, AllocValue* v, Snapshot* s);
429
430  // Arguments passed to AddIfNonLive
431  struct AddNonLiveArgs {
432    Snapshot* dest;
433    Snapshot* base;
434  };
435
436  // Helper for NonLiveSnapshot.  Adds the object to the destination
437  // snapshot if it is non-live.
438  static void AddIfNonLive(const void* ptr, AllocValue* v,
439                           AddNonLiveArgs* arg);
440
441  // Write contents of "*allocations" as a heap profile to
442  // "file_name".  "total" must contain the total of all entries in
443  // "*allocations".
444  static bool WriteProfile(const char* file_name,
445                           const Bucket& total,
446                           AllocationMap* allocations);
447
448  // data ----------------------------
449
450  // Memory (de)allocator that we use.
451  Allocator alloc_;
452  DeAllocator dealloc_;
453
454  // Overall profile stats; we use only the Stats part,
455  // but make it a Bucket to pass to UnparseBucket.
456  Bucket total_;
457
458  bool profile_mmap_;
459
460  // Bucket hash table for malloc.
461  // We hand-craft one instead of using one of the pre-written
462  // ones because we do not want to use malloc when operating on the table.
463  // It is only few lines of code, so no big deal.
464  Bucket** bucket_table_;
465  int num_buckets_;
466
467  // Map of all currently allocated objects and mapped regions we know about.
468  AllocationMap* address_map_;
469
470  DISALLOW_COPY_AND_ASSIGN(HeapProfileTable);
471};
472
473class HeapProfileTable::Snapshot {
474 public:
475  const Stats& total() const { return total_; }
476
477  // Report anything in this snapshot as a leak.
478  // May use new/delete for temporary storage.
479  // If should_symbolize is true, will fork (which is not threadsafe)
480  // to turn addresses into symbol names.  Set to false for maximum safety.
481  // Also writes a heap profile to "filename" that contains
482  // all of the objects in this snapshot.
483  void ReportLeaks(const char* checker_name, const char* filename,
484                   bool should_symbolize);
485
486  // Report the addresses of all leaked objects.
487  // May use new/delete for temporary storage.
488  void ReportIndividualObjects();
489
490  bool Empty() const {
491    return (total_.allocs == 0) && (total_.alloc_size == 0);
492  }
493
494 private:
495  friend class HeapProfileTable;
496
497  // Total count/size are stored in a Bucket so we can reuse UnparseBucket
498  Bucket total_;
499
500  // We share the Buckets managed by the parent table, but have our
501  // own object->bucket map.
502  AllocationMap map_;
503
504  Snapshot(Allocator alloc, DeAllocator dealloc) : map_(alloc, dealloc) {
505    memset(&total_, 0, sizeof(total_));
506  }
507
508  // Callback used to populate a Snapshot object with entries found
509  // in another allocation map.
510  inline void Add(const void* ptr, const AllocValue& v) {
511    map_.Insert(ptr, v);
512    total_.allocs++;
513    total_.alloc_size += v.bytes;
514  }
515
516  // Helpers for sorting and generating leak reports
517  struct Entry;
518  struct ReportState;
519  static void ReportCallback(const void* ptr, AllocValue* v, ReportState*);
520  static void ReportObject(const void* ptr, AllocValue* v, char*);
521
522  DISALLOW_COPY_AND_ASSIGN(Snapshot);
523};
524
525#endif  // BASE_HEAP_PROFILE_TABLE_H_
526