Home | History | Annotate | Download | only in debug
      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 #ifndef BASE_DEBUG_LEAK_TRACKER_H_
      6 #define BASE_DEBUG_LEAK_TRACKER_H_
      7 
      8 #include "build/build_config.h"
      9 
     10 // Only enable leak tracking in non-uClibc debug builds.
     11 #if !defined(NDEBUG) && !defined(__UCLIBC__)
     12 #define ENABLE_LEAK_TRACKER
     13 #endif
     14 
     15 #ifdef ENABLE_LEAK_TRACKER
     16 #include "base/containers/linked_list.h"
     17 #include "base/debug/stack_trace.h"
     18 #include "base/logging.h"
     19 #endif  // ENABLE_LEAK_TRACKER
     20 
     21 // LeakTracker is a helper to verify that all instances of a class
     22 // have been destroyed.
     23 //
     24 // It is particularly useful for classes that are bound to a single thread --
     25 // before destroying that thread, one can check that there are no remaining
     26 // instances of that class.
     27 //
     28 // For example, to enable leak tracking for class net::URLRequest, start by
     29 // adding a member variable of type LeakTracker<net::URLRequest>.
     30 //
     31 //   class URLRequest {
     32 //     ...
     33 //    private:
     34 //     base::LeakTracker<URLRequest> leak_tracker_;
     35 //   };
     36 //
     37 //
     38 // Next, when we believe all instances of net::URLRequest have been deleted:
     39 //
     40 //   LeakTracker<net::URLRequest>::CheckForLeaks();
     41 //
     42 // Should the check fail (because there are live instances of net::URLRequest),
     43 // then the allocation callstack for each leaked instances is dumped to
     44 // the error log.
     45 //
     46 // If ENABLE_LEAK_TRACKER is not defined, then the check has no effect.
     47 
     48 namespace base {
     49 namespace debug {
     50 
     51 #ifndef ENABLE_LEAK_TRACKER
     52 
     53 // If leak tracking is disabled, do nothing.
     54 template<typename T>
     55 class LeakTracker {
     56  public:
     57   ~LeakTracker() {}
     58   static void CheckForLeaks() {}
     59   static int NumLiveInstances() { return -1; }
     60 };
     61 
     62 #else
     63 
     64 // If leak tracking is enabled we track where the object was allocated from.
     65 
     66 template<typename T>
     67 class LeakTracker : public LinkNode<LeakTracker<T> > {
     68  public:
     69   LeakTracker() {
     70     instances()->Append(this);
     71   }
     72 
     73   ~LeakTracker() {
     74     this->RemoveFromList();
     75   }
     76 
     77   static void CheckForLeaks() {
     78     // Walk the allocation list and print each entry it contains.
     79     size_t count = 0;
     80 
     81     // Copy the first 3 leak allocation callstacks onto the stack.
     82     // This way if we hit the CHECK() in a release build, the leak
     83     // information will be available in mini-dump.
     84     const size_t kMaxStackTracesToCopyOntoStack = 3;
     85     StackTrace stacktraces[kMaxStackTracesToCopyOntoStack];
     86 
     87     for (LinkNode<LeakTracker<T> >* node = instances()->head();
     88          node != instances()->end();
     89          node = node->next()) {
     90       StackTrace& allocation_stack = node->value()->allocation_stack_;
     91 
     92       if (count < kMaxStackTracesToCopyOntoStack)
     93         stacktraces[count] = allocation_stack;
     94 
     95       ++count;
     96       if (LOG_IS_ON(ERROR)) {
     97         LOG_STREAM(ERROR) << "Leaked " << node << " which was allocated by:";
     98         allocation_stack.OutputToStream(&LOG_STREAM(ERROR));
     99       }
    100     }
    101 
    102     CHECK_EQ(0u, count);
    103 
    104     // Hack to keep |stacktraces| and |count| alive (so compiler
    105     // doesn't optimize it out, and it will appear in mini-dumps).
    106     if (count == 0x1234) {
    107       for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
    108         stacktraces[i].Print();
    109     }
    110   }
    111 
    112   static int NumLiveInstances() {
    113     // Walk the allocation list and count how many entries it has.
    114     int count = 0;
    115     for (LinkNode<LeakTracker<T> >* node = instances()->head();
    116          node != instances()->end();
    117          node = node->next()) {
    118       ++count;
    119     }
    120     return count;
    121   }
    122 
    123  private:
    124   // Each specialization of LeakTracker gets its own static storage.
    125   static LinkedList<LeakTracker<T> >* instances() {
    126     static LinkedList<LeakTracker<T> > list;
    127     return &list;
    128   }
    129 
    130   StackTrace allocation_stack_;
    131 };
    132 
    133 #endif  // ENABLE_LEAK_TRACKER
    134 
    135 }  // namespace debug
    136 }  // namespace base
    137 
    138 #endif  // BASE_DEBUG_LEAK_TRACKER_H_
    139